You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加依赖后AWS SAM TypeScript应用EsBundleStep失败的解决方法

AWS SAM + TypeScript 引入 hanzi-tools 构建失败问题解决

问题现象

使用TypeScript开发AWS SAM应用,引入hanzi-tools库后,执行sam build时出现依赖解析错误;将报错依赖标记为外部后构建成功,但运行Lambda时失败。

相关代码与配置

Lambda 代码

import fetch from 'node-fetch';
import { Headers } from 'node-fetch';
var pinyinify = require("hanzi-tools").pinyinify;


type AnkiCardField = {
    value: string
}

type AnkiCardFields = {
    Front: AnkiCardField;
    Back: AnkiCardField;
    Pinyin: AnkiCardField;
}

type AnkiCard = {
    cardId: number;
    fields: AnkiCardFields;
}

function getListOfCards() {

    console.log(process.env.ANKI_CONNECT_URL);
    console.log(process.env.ANKI_CONNECT_PORT);
    let endpoint = 'http://' + process.env.ANKI_CONNECT_URL + ':' + process.env.ANKI_CONNECT_PORT;

    var requestHeaders = new Headers();
    requestHeaders.append("Content-Type", "application/json");
    var requestBody = JSON.stringify({ "action": "findCards", "version": 6, "params": { "query": "deck:TestDeck" } });
    var requestOptions = {
        method: 'POST',
        headers: requestHeaders,
        body: requestBody
    };

    fetch(endpoint, requestOptions)
        .then(response => response.json())
        .then(ankiFindCardsResponse => {
            var cardIdArray = ankiFindCardsResponse.result;
            var cardInfoRequestBody = JSON.stringify({ "action": "cardsInfo", "version": 6, "params": { "cards": cardIdArray } });
            var cardInfoRequestOptions = {
                method: 'POST',
                headers: requestHeaders,
                body: cardInfoRequestBody
            };
            return fetch(endpoint, cardInfoRequestOptions);
        })
        .then(cardInfoResponse => cardInfoResponse.json())
        .then(cardInfoResponseJson => {
            var ankiCardArray = cardInfoResponseJson.result;
            let singleCharacterMap = new Map<string, AnkiCard>();
            let multipleChararacterCards: AnkiCard[] = [];
            let singleCardCount = 0;
            let doubleCardCount = 0;
            console.log(`总共有 ${ankiCardArray.length} 张卡片`);

            ankiCardArray.forEach((ankiCard: AnkiCard) => {
                let chinese: string = ankiCard.fields.Front.value;
                if (chinese.length == 1) {
                    singleCharacterMap.set(chinese, ankiCard);
                    singleCardCount++;
                } else {
                    multipleChararacterCards.push(ankiCard);
                    doubleCardCount++;
                }
            });
            console.log(`总共有 ${singleCardCount} 张单字卡片`);
            console.log(`总共有 ${doubleCardCount} 张多字卡片`);


            multipleChararacterCards.forEach((multiCharacterCard: AnkiCard) => {
                let chinese: string = multiCharacterCard.fields.Front.value;
                chinese.split('').forEach(chineseCharacter => {
                    if (!singleCharacterMap.has(chineseCharacter)) {
                        //pinyinify(chineseCharacter);
                        console.log(`${chineseCharacter} 需要添加`)
                    }
                });

            });
        }

        )
        .catch(error => console.log('错误', error));


}


export const lambdaHandler = (event: Object): String => {
    getListOfCards();
    return "完成";

};

构建错误信息

执行sam build时抛出以下错误:

Error: NodejsNpmEsbuildBuilder:EsbuildBundle - Esbuild Failed: ✘ [ERROR] Could not resolve "mock-aws-s3"

node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28:
  43 │     const AWSMock = require('mock-aws-s3');
     ╵                             ~~~~~~~~~~~~~

You can mark the path "mock-aws-s3" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.

✘ [ERROR] Could not resolve "aws-sdk"

node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22:
  76 │   const AWS = require('aws-sdk');
     ╵                       ~~~~~~~~~

You can mark the path "aws-sdk" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.

✘ [ERROR] Could not resolve "nock"

node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23:
  112 │   const nock = require('nock');
      ╵                        ~~~~~~

You can mark the path "nock" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.

package.json 配置

{
  "name": "hello_world",
  "version": "1.0.0",
  "description": "hello world sample for NodeJS",
  "main": "app.js",
  "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs",
  "author": "SAM CLI",
  "license": "MIT",
  "scripts": {
    "unit": "jest",
    "lint": "eslint '*.ts' --quiet --fix",
    "compile": "tsc",
    "test": "npm run compile && npm run unit"
  },
  "devDependencies": {
    "@types/aws-lambda": "^8.10.92",
    "@types/jest": "^29.2.0",
    "@types/node": "^18.11.4",
    "@types/node-fetch": "^2.6.2",
    "@typescript-eslint/eslint-plugin": "^5.10.2",
    "@typescript-eslint/parser": "^5.10.2",
    "esbuild-jest": "^0.5.0",
    "eslint": "^8.8.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
    "jest": "^29.2.1",
    "prettier": "^2.5.1",
    "ts-node": "^10.9.1",
    "typescript": "^4.8.4"
  },
  "dependencies": {
    "esbuild": "^0.14.14",
    "hanzi-tools": "^1.2.26",
    "node-fetch": "^2.6.1"
  }
}

template.yaml 配置

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  anki-connect-lambda

  Sample SAM Template for anki-connect-lambda

Globals:
  Function:
    Timeout: 60
    Tracing: Active
  Api:
    TracingEnabled: true

Resources:
  AnkiConnectLambda:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: lambda/
      Handler: app.lambdaHandler
      Runtime: nodejs18.x
      MemorySize: 2048
      Architectures:
      - x86_64
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
      Environment:
        Variables:
          ANKI_CONNECT_URL : localhost
          ANKI_CONNECT_PORT: 8765
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: es2020
        Sourcemap: true
        EntryPoints:
        - app.ts


  ApplicationResourceGroup:
    Type: AWS::ResourceGroups::Group
    Properties:
      Name:
        Fn::Join:
        - ''
        - - ApplicationInsights-SAM-
          - Ref: AWS::StackName
      ResourceQuery:
        Type: CLOUDFORMATION_STACK_1_0
  ApplicationInsightsMonitoring:
    Type: AWS::ApplicationInsights::Application
    Properties:
      ResourceGroupName:
        Fn::Join:
        - ''
        - - ApplicationInsights-SAM-
          - Ref: AWS::StackName
      AutoConfigurationEnabled: 'true'
    DependsOn: ApplicationResourceGroup
Outputs:
  HelloWorldApi:
    Description: API Gateway生产环境Hello World函数端点URL
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  AnkiConnectLambda:
    Description: Hello World Lambda函数ARN
    Value: !GetAtt AnkiConnectLambda.Arn
  AnkiConnectLambdaIamRole:
    Description: Hello World函数自动创建的IAM角色
    Value: !GetAtt AnkiConnectLambdaRole.Arn

问题原因

hanzi-tools依赖的包使用了@mapbox/node-pre-gyp,而@mapbox/node-pre-gyp引入了mock-aws-s3、aws-sdk、nock这些仅用于开发测试的依赖。esbuild打包时会尝试解析所有依赖,导致构建失败;标记为外部后,打包时会忽略这些依赖,但Lambda运行环境中没有这些包,触发运行时错误。

解决方案

方案1:配置esbuild排除无关依赖

修改template.yaml中的BuildProperties,添加External字段排除这些仅用于测试的依赖。因为这些依赖是@mapbox/node-pre-gyp的测试代码,正常运行hanzi-tools功能时不会被调用,所以可以安全排除:

Metadata:
  BuildMethod: esbuild
  BuildProperties:
    Minify: true
    Target: es2020
    Sourcemap: true
    EntryPoints:
    - app.ts
    External:
    - mock-aws-s3
    - aws-sdk
    - nock

方案2:替换为纯JavaScript替代库

寻找功能类似的纯JavaScript汉字处理库,避免引入依赖node-pre-gyp的包,从根源解决依赖问题。

方案3:生产环境安装时排除开发依赖

在构建部署前执行npm install --omit=dev,确保不会安装开发依赖,但需注意区分本地开发和生产构建流程,避免影响开发环境。

验证步骤

  1. 修改template.yaml添加External配置后,重新执行sam build
  2. 部署Lambda后测试功能,确认hanzi-tools的pinyinify函数能正常调用

内容的提问来源于stack exchange,提问作者KennyBartMan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 14:47:02