aws-sam-cli: Bug: Wrong handler mapping after esbuild

Description:

When running esbuild on a Lambda function, the output doesn’t mantain the same folder structure as the enry points, and this remains unchange in the built template, resulting in SAM not finding the lambda Handler:

Steps to reproduce:

Project structure:

- package.json
- template.yaml
-- src
--- functions
---- FunctionName
----- app.js
--- utils
---- some-util.js

Template.yaml:

FunctionName:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: esbuild
    Properties:
      Handler: src/functions/FunctionName/app.Handler
      Events:
        HttpEvent:
          ....

Observed result:

code is budled to FunctionName folder in .aws-build resulting in:

- .aws-build
-- build
--- template.yaml
--- FunctionName
---- app.js

However, the built template results in:

FunctionName:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: esbuild
    Properties:
      CodeUri: FunctionName
      Handler: src/functions/FunctionName/app.Handler
      Events:
        HttpEvent:
          ....

Resulting in an error since FunctionName/src/functions/FunctionName/app.Handler

Expected result:

Handler in built template should match esbuild output. I.E:

FunctionName:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: esbuild
    Properties:
      CodeUri: FunctionName
      Handler: app.Handler
      Events:
        HttpEvent:
          ....

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 17 (9 by maintainers)

Most upvoted comments

I’ve found a similar problem when using regular sam init (v 1.57):

Quick Start, Hello World, nodejs16.x, Zip, Typescript.

First thing to note is that if I invoke sam build and then sam local start-api -d 3099 and then attach my debugger as I invoke a curl command, I am able to set breakpoints in hello-world/app.ts and step through with my debugger.

If I then add two subfolders:

- template.yaml
- hello-world
-- package.json
-- app.ts
-- node_modules
-- hello1
--- app.ts
-- hello2
--- app.ts

Then I’m faced with a conundrum:

  • If, for each AWS::Serverless::Function, I point CodeUri to a subfolder like /hello-world/hello1, it complains about not finding a package.json. It says package.json file not found. Bundling source without dependencies. The resultant app.js.map in .aws-sam does have an accurate sources entry though, e.g. "sources": ["../../../hello-world/hello1/app.ts"],

  • If I instead revert CodeUri to point to the folder with package.json, then I need to indicate the path to the new function a different way. If I choose Handler, e.g. Handler: hello1/app.lambdaHandler, (assuming Handler evaluates from the CodeUri directory), then it appears to build correctly but .aws-sam/build/HelloOneFunction includes the wrong source, meaning the source for HelloWorldFunction instead. Additionally, the app.js.map file refers to a file in private/var/folders that does not exist:

  "sources": ["../../../../../../../../../private/var/folders/kw/x3x7dz_j07v_b98j6dy7hbx4blmgmx/T/tmpclbl29bx/app.ts"],

(everything up through the /T exists, but the tmp... folder does not.)

  • If I instead refer to the path in EntryPoints, e.g. hello2/app.ts, then .aws-sam/build/HelloTwoFunction/app.js includes the correct source, but its app.js.map again points to a file in private/var/folders that does not exist.

If I leave those folders in place in the filesystem, but remove them from template.yaml, then I can still set breakpoints on the original hello-world/app.ts function. However, if I add AWS::Serverless::Function definitions for either of the two new subfolder functions into template.yaml, then the breakpoints don’t work anymore - not even on the original hello-world/app.ts function.

I believe that for codebases with multiple functions, it would be preferable to share one package.json that is from a common parent folder, and to also be able to use a debugger on these functions.

@mildaniel any news on this?

I did some digging and found the issue is with how we bundle. I’ve explained it in #4216, but essentially we bundle straight into the artifact directory, at which point we’ve already lost the file tree from the code URI to the handler. e.g. esbuild takes the entry point (src/functions/FunctionName/app.Handler) and bundles that into a single file (app.js) then takes that file and moves it into the artifact directory FunctionName

@lucashuy Actually, this is something that is even included in one of SAM’s examples https://github.com/aws/aws-sam-cli-app-templates/tree/master/nodejs16.x/cookiecutter-quick-start-web so how come it is not actually supported? Furthermore, this limits us to only having the possibility to have the handler at the root level of the codeUri (You can’t have something like ./src/app.js for example, because the output from esbuild would be wrongly mapped) And sharing a single node_modules at root level implies Only one npm install is run for all of your lambda functions, which would make the build process much faster.

Hi, thanks for opening this issue! Having root level node_modules is not a case that SAM CLI supports right now. The solution would be to set the CodeUri according to your folder structure and Handler to your functions like mentioned previously.

Please feel free to reopen or open a new issue if anything else arises.