SAM deploying TypeScript source code instead of the transpiled JavaScript
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:
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!
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:
- Why is
samconfig.tomlnot in the relative path of the SAM CLI when I executesam deploy ...from the same directory? I've never had to set the absolute path to thesamconfig.tomlfile before. - 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.yamlbefore.
To clarify, here is the process I am following to deploy my code:
- Build the stack
webpack-cli --env prod - 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.
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.