VS Code 中 AWS SAM 无服务器应用程序的 TypeScript 调试

TypeScript debugging of AWS SAM serverless apps in VS Code

我有一个在 Visual Studio 代码中使用 AWS SAM 构建的无服务器函数。我使用的 运行time 是 nodejs12.x 但我正在用 TypeScript 编写所有内容,然后将其编译为 JS 到 /dist 目录中。这是我将所有 CloudFormation 模板指向的目录,以便找到处理程序。比如右边是TS,左边是我编译的JS。

在侧边栏中,您可以在 运行 tsc 之后看到我的 JS 文件所在的 /dist 目录,再往下一点是我的模板和 TypeScript 源代码。

我的模板看起来像这样:

  LibraryAddMangaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: dist/src/handlers.LibraryAddMangaHandler
      FunctionName: Foobar
      Runtime: nodejs12.x
      MemorySize: 256
      Timeout: 300

我按照 Amazon's documentation 在 VS Code 中执行本地调试,并使其能够 用于 JS 文件 。我可以毫无问题地设置断点并单步执行已编译的 JS 代码。

我想知道是否可以在单步执行 TypeScript 代码时进行调试,类似于我在 Chrome 中对客户端应用程序在 Angular/React 中所做的事情。我知道这些框架会处理 JS 到 TS 的映射,作为构建过程的一部分,以支持 Chrome 调试工具的需求。如果没有 SAM/AWS.

的支持,我不确定我想做的事情是否可行

此外,当我的 HTTP 请求完成时,调试会话结束,而不是继续 运行 并侦听下一个请求 - 类似于 sam local start-api。有没有办法配置调试 运行ner 以保持应用程序 运行ning?我看到一些建议使用 Thundra,但这需要将 Lambda 部署到 AWS 中,我真的很喜欢本地调试、调整、调试的快速循环。我真的不想每 15 秒执行一次 sam deploy 并等待堆栈部署。

谢谢!

是的,可以做到:你需要的是sourcemaps

在我的例子中,我将我的 lambda 表达式编译并捆绑到 dist 文件夹下的 1 个 index.js 文件中。我使用 webpack 作为捆绑器将其与单个 .map 文件一起发送。

const path = require('path');
const glob = require('glob');
const webpack = require('webpack');
const CreateFileWebpack = require('create-file-webpack');
const nodeExternals = require('webpack-node-externals');

// Credits: https://hackernoon.com/webpack-creating-dynamically-named-outputs-for-wildcarded-entry-files-9241f596b065
const entryArray = glob.sync('./src/lambda/**/handler.ts');

const entryObject = entryArray.reduce((acc, item) => {
  let name = path.dirname(item.replace('./src/lambda', ''));
  // conforms with Webpack entry API
  // Example: { ingest: './src/ingest/index.ts' }
  acc[name] = item;
  return acc;
}, {});

/** @type {import('webpack').Configuration} */
module.exports = {
  cache: {
    type: 'memory',
  },
  entry: entryObject,
  devtool: false,
  target: 'node',
  // externals: [nodeExternals()],  // use if dependencies should not be bundled (like when using layers)
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          transpileOnly: true,
        },
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  plugins: [
    new webpack.IgnorePlugin({ resourceRegExp: /^pg-native$/ }),
    new webpack.IgnorePlugin({ resourceRegExp: /^hiredis$/ }),
    ...Object.keys(entryObject).map(lambda => {
      return new CreateFileWebpack({
        path: path.resolve(__dirname, 'build'),
        fileName: `${lambda}/package.json`,
        content: JSON.stringify({
          name: 'dummy_dependencies',
          dependencies: {},
          version: '1.0.0',
        }),
      });
    }),
    new webpack.SourceMapDevToolPlugin({
      columns: false,
      module: true,
      filename: '[file].map',
    }),
  ],
  // Output directive will generate build/<function-name>/index.js
  output: {
    filename: '[name]/index.js',
    path: path.resolve(__dirname, 'build'),
    devtoolModuleFilenameTemplate: '[absolute-resource-path]',
    // Credit to Richard Buggy!!
    libraryTarget: 'commonjs2',
  },
};

我不知道你有多个编译文件会怎样,但我认为它与 webpack 或任何其他“编译”工具的情况类似(我最近一直在研究 esbuild).

即使是激活了 sourcemaps 的普通 tsc 也应该足够了。

完成后,您还应该使用正确的 .vscode 启动配置,以便 vs 代码可以将容器的远程文件映射到本地文件,同时还可以通过 sourcemaps 连接其相应的 typescript 文件。

    {
      "name": "[Serverless] <lambda-name> attach",
      "type": "node",
      "request": "attach",
      "address": "localhost",
      "port": 5678,
      "localRoot": "${workspaceRoot}/.serverless/build/<lambda-folder>",
      "remoteRoot": "/var/task",
      "protocol": "inspector",
      "stopOnEntry": false,
      "outFiles": ["${workspaceRoot}/.serverless/build/<lambda-folder>"],
      "sourceMaps": true
    }

Is there a way to configure the debug runner so it keeps the app running?

如果我没记错的话,你要找的是在请求之间保持调试器连接这样你就不必在每次调用时都手动连接它本地的 lambda。

如果是这种情况,您可以通过 运行 sam local start-lambda 使用 --shutdown--warm-containers LAZY 标志 (reference) 来实现:

sam local start-lambda -d 5678 --host 0.0.0.0 --shutdown --debug --warm-containers LAZY

这将创建一个类似于 AWS 在云上使用的 api,因此您可以使用 aws-sdkaws cli

来命中它
aws lambda invoke --function-name \"<sam-template-lambda-id>\" --payload <stringified-json-input> --endpoint-url \"http://127.0.0.1:3001\" lambda_invoke_output.log

aws sam 将保持容器温暖,直到 lambda 代码发生变化,并且调试器将在您针对它触发的所有 INVOKE 事件中保持激活状态,有效地停止在 typescript 断点上每次调用 lambda

时设置

TL;DR

使用 sourcemaps,并在整个互联网上搜索如何将所有东西粘合在一起


个人说明:我第一次处理这个问题时,我在互联网上费了好大的劲才把它搞定。

生活不应该是这样的:')

如果你想在没有 webpack 的情况下做到这一点,现在有一种更简单的方法。

首先确保您正在制作源地图。 在你的tsconfig.js(不同的方法来做到这一点,我更喜欢in-line sourcemaps)

{
  "compilerOptions": {
    ...,
    "inlineSourceMap": true,
    ...
  },
  ...
}

安装 VSCode 的 AWS Tookit 扩展。 现在,在您的 template.yaml 文件中,您将拥有一个生成调试配置的魔法按钮 =]