I'm trying to get typescript running on AWS sam local using webpack to transpile but because of all the extra webpack output, sam local is unable to find my exported handler function.
If I just run tsc and transpile my code the lambda function runs fine, so my question is it possible to transpile typescript using webpack without it adding all the extra webpack module stuff? Or if not do you have any other suggestions to get my build to be compatible with AWS sam-cli
The error I get with my current code is TypeError: handler is not a function
This is my webpack output
exports["handler"] =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return
installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the
cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module,
module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter)
{
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' &&
Symbol.toStringTag) {
/******/ Object.defineProperty(exports,
Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', {
value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' &&
value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', {
enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var
key in value) __webpack_require__.d(ns, key, function(key) { return
value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-
harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return
module['default']; } :
/******/ function getModuleExports() { return
module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) {
return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s =
"./src/lambda/users/register/index.ts");
/******/ })
/**************************************************************/
/******/ ({
/***/ "./src/lambda/users/register/index.ts":
/*!********************************************!*\
!*** ./src/lambda/users/register/index.ts ***!
\********************************************/
/*! exports provided: handler */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export
(binding) */ __webpack_require__.d(__webpack_exports__, \"handler\",
function() { return handler; });\n\nvar __awaiter = (undefined &&
undefined.__awaiter) || function (thisArg, _arguments, P, generator)
{\n return new (P || (P = Promise))(function (resolve, reject)
{\n function fulfilled(value) { try {
step(generator.next(value)); } catch (e) { reject(e); } }\n
function rejected(value) { try { step(generator[\"throw\"](value));
} catch (e) { reject(e); } }\n function step(result) {
result.done ? resolve(result.value) : new P(function (resolve) {
resolve(result.value); }).then(fulfilled, rejected); }\n
step((generator = generator.apply(thisArg, _arguments ||
[])).next());\n });\n};\nvar __generator = (undefined &&
undefined.__generator) || function (thisArg, body) {\n var _ = {
label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1];
}, trys: [], ops: [] }, f, y, t, g;\n return g = { next: verb(0),
\"throw\": verb(1), \"return\": verb(2) }, typeof Symbol ===
\"function\" && (g[Symbol.iterator] = function() { return this; }),
g;\n function verb(n) { return function (v) { return step([n,
v]); }; }\n function step(op) {\n if (f) throw new
TypeError(\"Generator is already executing.\");\n while (_)
try {\n if (f = 1, y && (t = y[op[0] & 2 ? \"return\" :
op[0] ? \"throw\" : \"next\"]) && !(t = t.call(y, op[1])).done)
return t;\n if (y = 0, t) op = [0, t.value];\n
switch (op[0]) {\n case 0: case 1: t = op; break;\n
case 4: _.label++; return { value: op[1], done: false };\n
case 5: _.label++; y = op[1]; op = [0]; continue;\n
case 7: op = _.ops.pop(); _.trys.pop(); continue;\n
default:\n if (!(t = _.trys, t = t.length > 0 &&
t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue;
}\n if (op[0] === 3 && (!t || (op[1] > t[0] &&
op[1] < t[3]))) { _.label = op[1]; break; }\n if
(op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break;
}\n if (t[2]) _.ops.pop();\n
_.trys.pop(); continue;\n }\n op =
body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; }
finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return {
value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar _this =
undefined;\nvar handler = function (event) {\n if (event === void
0) { event = {}; }\n return __awaiter(_this, void 0, void 0,
function () {\n var response;\n return
__generator(this, function (_a) {\n console.log('Hello
World!');\n response = JSON.stringify(event, null, 2);\n
return [2 /*return*/, 'success'];\n });\n });\n};\n\n\n//#
sourceURL=webpack://handler/./src/lambda/users/register/index.ts?");
/***/ })
/******/ });
This is the original src code
'use strict'
export const handler = async (event: any = {}): Promise<any> => {
console.log('Hello World!');
const response = JSON.stringify(event, null, 2);
return response;
}
This is the tsc compiled code that runs ( I do get an error but i'm aware of why that is happening and it is not an issue to fix)
'use strict';
var __awaiter = (this && this.__awaiter) || function (thisArg,
_arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try {
step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"] .
(value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value)
: new P(function (resolve) { resolve(result.value);
}).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments ||
[])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg,
body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw
t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2)
}, typeof Symbol === "function" && (g[Symbol.iterator] = function()
{ return this; }), g;
function verb(n) { return function (v) { return step([n, v]); };
}
function step(op) {
if (f) throw new TypeError("Generator is already
executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ?
y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t
= t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done:
false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length
- 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] &&
op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label =
t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2];
_.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] :
void 0, done: true };
}
};
var _this = this;
exports.__esModule = true;
exports.handler = function (event) {
if (event === void 0) { event = {}; }
return __awaiter(_this, void 0, void 0, function () {
var response;
return __generator(this, function (_a) {
console.log('Hello World!');
response = JSON.stringify(event, null, 2);
return [2 /*return*/, 'success'];
});
});
};
I've tried multiple different libraryTargets but none of them worked
Good question for a new user! Here's what (I think) would be the solution.
By default, if you import some library in your source code, Webpack will include it in the bundle it produces. That is pretty neat for the web, because you can build a whole app that uses many packages and bundle the whole thing into one single .js file and serve it to the people visiting your site. Webpack can do many thing, but this is why it is a web bundler.
When building cloud functions, however, it's different. The servers that will serve your web functions already have the libs you'll be using, contrary to some random people visiting a website. You can tell AWS what libs you'll be using a standard package.json
as long as I remember.
So, how to tell webpack not to include the libraries that the server will already "know about"? Simple anser, use the external parameter in your webpack.config.js
. Example:
// in webpack.config.js module.exports = { // ... externals: { react: 'react', // tells webpack not to include the react package inside its bundle } }
That is, by the way, why tsc
"works", as it only transpiles the code, but doesn't bundle everything that your source code needs inside one file.