In the process of developing a basic Hello World function for AWS Lambda/APIGateway, I aim to integrate ESLint and Typescript features. Typically, when working with Typescript and ESlint, inclusion of the eslint-plugin-import
package and specifying the extensions in my .eslintrc
file is required:
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
}
In this particular monorepo setup, despite having these configurations in place, I still encounter the
Unable to resolve path to module 'aws-lambda'
error. Here's how my repository is structured:
apis/
users/
dist/ <-- My compiled JS code
node_modules/
src/
functions/ <-- My Typescript Lambda functions
test/ <-- My Jest tests
.eslintrc <-- My ESLint config
package.json
template.yaml <-- My SAM template
tsconfig.json <-- My Typescript config
webpack.config.js <-- My WebPack config
Below is the content of my .eslintrc
file:
{
"root": true,
"env": {
"es2020": true,
"mongo": true,
"node": true,
"jest": true
},
"parser": "@typescript-eslint/parser",
"extends": [
"airbnb",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended" // This will display prettier errors as ESLint errors. Make sure this is always the last configuration.
],
"ignorePatterns": ["dist/", "coverage/", "test/"],
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"impliedStrict": true
}
},
"rules": {
"import/extensions": [
"error",
"ignorePackages",
{
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}
],
"max-len": [
"error",
{
"code": 100,
"ignoreComments": true,
"ignoreStrings": true,
"ignoreTemplateLiterals": true
}
],
"quotes": ["error", "single", { "allowTemplateLiterals": true }],
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"vars": "all",
"args": "none"
}
]
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
}
}
This snippet showcases my tsconfig.json settings:
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "esnext",
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist"
},
"include": ["src/**/*"],
"exclude": ["**/*.test.*"]
}
Last but not least, here is my webpack configuration:
/* eslint-disable */
const path = require('path');
const { readFileSync } = require('fs');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const { yamlParse } = require('yaml-cfn');
/* eslint-enable */
const conf = {
prodMode: process.env.NODE_ENV === 'production',
templatePath: './template.yaml',
};
const cfn = yamlParse(readFileSync(conf.templatePath));
const entries = Object.values(cfn.Resources)
// Find nodejs functions
.filter((resource) => resource.Type === 'AWS::Serverless::Function')
.filter(
(resource) =>
(resource.Properties.Runtime && resource.Properties.Runtime.startsWith('nodejs')) ||
(!resource.Properties.Runtime && cfn.Globals.Function.Runtime)
)
.map((resource) => ({
// Isolate handler src filename
handlerFile: resource.Properties.Handler.split('.')[0],
// Build handler dst path
CodeUriDir: resource.Properties.CodeUri.split('/').splice(1).join('/')
}))
.reduce(
(resources, resource) =>
Object.assign(
resources,
// Generate {outputPath: inputPath} object
{ [`${resource.CodeUriDir}/${resource.handlerFile}`]: `./src/${resource.CodeUriDir}.ts` }
),
{}
);
module.exports = {
entry: entries,
target: 'node',
mode: conf.prodMode ? 'production' : 'development',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader'
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
libraryTarget: 'commonjs2'
},
devtool: 'source-map',
plugins: conf.prodMode
? [
new UglifyJsPlugin({
parallel: true,
extractComments: true,
sourceMap: true
})
]
: []
};