StackToolbox / aws-sam-webpack-plugin

A Webpack plugin to replace the build step for SAM CLI
MIT License
147 stars 29 forks source link

SAM deploying TypeScript source code instead of the transpiled JavaScript #86

Open seansica opened 1 year ago

seansica commented 1 year ago

My build process successfully outputs webpack .js artifacts. However, when I deploy to AWS, only the original TypeScript files get uploaded, not the transpiled .js and .map.js files shown below. I cannot for the life of me figure out why this is happening.

Running webpack-cli produces:

.aws-sam
└── build
    ├── ConfigFunction
    │   ├── index.js
    │   └── index.js.map
    ├── DiscordNotifierFunction
    │   ├── index.js
    │   └── index.js.map
    ├── QueuePublisherFunction
    │   ├── index.js
    │   └── index.js.map
    ├── ShopifySyncFunction
    │   ├── index.js
    │   └── index.js.map
    └── template.yaml

But when I deploy the stack, the deployed lambda packages only include the orginal TypeScript files, not the webpack bundles.

Example: image

Ignore the fact that the file is named app instead of index -- this is because I've been trying different things and happened to grab a screenshot before I switched to using index. The important part is that the .ts source code is getting deployed instead of the .js files that are generated in the .aws-sam/build directory.

Here is my webpack config. Note that I added an --env flag to webpack to more easily switch between template.prod.yaml and template.dev.yaml.

webpack.config.cjs:

const path = require("path");
const AwsSamPlugin = require("aws-sam-webpack-plugin");

const getEnvironment = (env) => {
    if (env.prod) {
        return 'prod';
    }
    if (env.dev) {
        return 'dev';
    }
    throw new Error('No valid Webpack environment set');
};

module.exports = (env) => {
    const environment = getEnvironment(env);
    const awsSamPlugin = new AwsSamPlugin({
        projects: {
            [environment]: `./template.${environment}.yaml`,
        },
        outFile: "index"
    });
    return {
        entry: () => awsSamPlugin.entry(),
        output: {
            filename: (chunkData) => awsSamPlugin.filename(chunkData),
            libraryTarget: "commonjs2",
            path: path.resolve("."),
        },
        devtool: "source-map",
        resolve: {
            extensions: [".ts", ".js"],
        },
        target: "node",
        externals: process.env.NODE_ENV === "development" ? [] : ["aws-sdk"],
        mode: process.env.NODE_ENV || "production",
        module: {
            rules: [{ test: /\.tsx?$/, loader: "ts-loader" }],
        },
        plugins: [awsSamPlugin],
    };
};

To deploy, I run sam deploy --config-env dev or sam deploy --config-env default.

samconfig.toml:

version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "shopify-bot"
s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-1n1zoqn89o0dg"
s3_prefix = "shopify-bot"
region = "us-east-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
image_repositories = []
profile = "AdministratorAccess-328571739532"
template_file = "template.prod.yaml"

[dev]
[dev.deploy]
[dev.deploy.parameters]
stack_name = "shopify-bot-dev"
s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-csuag1qx37c0"
s3_prefix = "shopify-bot-dev"
region = "us-east-2"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
image_repositories = []
profile = "AdministratorAccess-328571739532"
template_file = "template.dev.yaml"

Here is how my lambda files are organized:

├── lambdas
│   ├── checkAllInventory
│   │   └── index.ts
│   ├── config
│   │   └── index.ts
│   ├── discordNotifier
│   │   └── index.ts
│   ├── queuePublisher
│   │   └── index.ts
│   └── shopifySync
│       └── index.ts
├── package-lock.json
├── package.json

Here are the CodeUri and Handler properties declared in my SAM templates:

Resources:

  ConfigFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ConfigFunction
      CodeUri: lambdas/config
      Handler: index.handler

  ShopifySyncFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ShopifySyncFunction
      CodeUri: lambdas/shopifySync
      Handler: index.handler

  QueuePublisherFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: QueuePublisherFunction
      CodeUri: lambdas/queuePublisher
      Handler: index.handler

  DiscordNotifierFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: DiscordNotifierFunction
      CodeUri: lambdas/discordNotifier
      Handler: index.handler

Any help in understanding why SAM is deploying TypeScript files despite webpack correctly outputting ECMAScript files to .aws-sam/build/ would be appreciated!

seansica commented 1 year ago

So I found an interesting workaround:

sam deploy --template-file .aws-sam/build/template.yaml --config-file ~/dev/shopify-monitor/samconfig.toml --config-env dev --guided

For some reason, specifying the relative path to the template and the absolute path to samconfig.toml resolved the issue. SAM is deploying the transpiled .js code from .aws-sam/build/ now.

I tried passing a relative path to samconfig.toml but that did not work:

> sam deploy --template-file .aws-sam/build/template.yaml --config-file ./samconfig.toml --config-env dev --guided                    

Error: Config file ./samconfig.toml does not exist or could not be read!

This leaves some outstanding questions:

  1. Why is samconfig.toml not in the relative path of the SAM CLI when I execute sam deploy ... from the same directory? I've never had to set the absolute path to the samconfig.toml file before.
  2. Same question. Why do I need to explicitly specify the path to .aws-sam/build/template.yaml? I've never had to set the path to .aws-sam/build/template.yaml before.

To clarify, here is the process I am following to deploy my code:

  1. Build the stack webpack-cli --env prod
  2. Deploy the stack: sam deploy --config-env dev

Step 1 definitively builds the intended stack. We can verify this by looking at the contents of .aws-sam/builld/ after Step 1 and checking that only bundled JS files are present. This is the case.

Step 2 seems to lack harmony with Step 1. While Step 2 technically works in that AWS SAM successfully deploys the defined resources to AWS, it somehow deploys the original TypeScript files instead of the JS bundles that were generated in Step 1.

buggy commented 1 year ago

Originally SAM CLI had separate steps for build, package and deploy. The plugin replaces the build step in that original 3 step process. My best guess is that it's incompatible with the newer guided deployment that sam deploy offers. That would make sense because SAM is going to use it's own build step instead of this plugin.