CJS Serverless项目运行时动态加载ESM模块@gsys/properties-node失败的排障求助
CJS Serverless项目运行时动态加载ESM模块@gsys/properties-node失败的排障求助
大家好,我目前在维护一个基于Serverless框架的项目,用serverless-bundle做代码打包,现在遇到了一个棘手的问题:运行时动态加载ESM模块@gsys/properties-node时,总是抛出模块找不到的错误。下面是我的项目配置、相关代码和错误信息,麻烦各位帮忙看看问题出在哪?
一、Serverless打包配置(serverless.yml片段)
我在serverless配置里针对@gsys/properties-node做了这些特殊处理:
sourcemaps: true disableForkTsChecker: true externals: - newrelic # Keeps newrelic untouched in node modules. Not compatible with webpack/treeshaking - '@js-temporal/polyfill' - '@gsys/common-events' forceInclude: - '@gsys/properties-node' copyFiles: - from: 'node_modules/@gsys/outer-limits-client/resources/limit-files/social-media-limits.yml' to: './src/main/ts/resources/limit-files' - from: 'node_modules/@gsys/kafka-clients-node/dist/resources/buckets.yml' to: './src/main/ts/resources/' custom: esbuild: bundle: true external: - '@gsys/properties-node' plugins: - serverless-bundle
因为这个模块是纯ESM格式的,我需要在运行时动态加载,所以把它加入了forceInclude确保被打包进去,同时在esbuild配置里设为external避免打包时的CJS/ESM互操作报错。
二、Handler核心代码
我的Lambda handler是一个异步函数,核心逻辑就是动态加载目标模块并初始化处理程序:
async function dynamicPropertiesHandler(event: FlyoverAlbEvent, awsContext: Context){ try { const correlationID = getCorrelationIdFromHeaders(event.headers ?? {}); log.setMetadata({ ...awsContext, correlationID }); log.info(`Incoming ${event.httpMethod} request for dynamic properties handler, PATH: ${event.path}`); const propertyService: any = await dynamicPropertyService.getService(); // 动态加载ESM模块 const { PropertiesHandler } = await loadEsm('@gsys/properties-node'); const propertiesHandler = new PropertiesHandler(propertyService).buildHandler(); const response: ALBResult | undefined = await propertiesHandler(event); log.info(`DynamicPropertiesHandler response: ${JSON.stringify(response)}`); if (!response) { throw new NotFoundError(PROPERTY_NOT_FOUND); } return response; } catch (error) { logError(log, error, 'error occured in dynamic property handler'); return generateALBError(error); } }
其中loadEsm是我封装的工具函数,内部用标准的import()语法实现ESM模块加载,本地测试时是正常的。
三、TypeScript配置(tsconfig.json)
项目的TS配置如下,我特意设置了module: Node16来支持ESM和CJS的互操作:
{ "extends": "@some-other-library", "compilerOptions": { "rootDir": "./", "esModuleInterop": true, "resolveJsonModule": true, "module": "Node16", "target": "ES2022", /* Strict Type-Checking Options */ "strict": true /* Enable all strict type-checking options. */, "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, "strictNullChecks": true /* Enable strict null checks. */, "strictFunctionTypes": true /* Enable strict checking of function types. */, "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, "pretty": true, /* Additional Checks */ "noUnusedLocals": true /* Report errors on unused locals. */, "noUnusedParameters": true /* Report errors on unused parameters. */, "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, "outDir": "dist", // Required for serverless "sourceMap": true, "useUnknownInCatchVariables": false, "lib": ["es2016", "es2017", "esnext", "dom"] } }
四、Package.json打包脚本片段
项目的打包脚本会先编译TS,再复制依赖配置并安装生产依赖:
{ "name": "my-project", "version": "1.0.0", "license": "ISC", "scripts": { "bundle": "rm -rf dist && mkdir dist && tsc --project tsconfig.bundle.json && cp package*.json ./dist && cp -r gsys-properties ./dist && cd ./dist && npm ci --no-progress --omit=dev && rm -f ../project.zip && zip -qr ../project.zip .", // 其他脚本省略... } }
五、运行时报错信息
部署到AWS Lambda后,请求触发时抛出的错误:
{"message":"Cannot find module '@gsys/properties-node'","code":"MODULE_NOT_FOUND","status":500,"contextId":null}
我已经做的排查(还没解决问题)
- 检查了部署用的zip包,确认
node_modules/@gsys/properties-node目录完整存在; - 尝试过去掉esbuild配置里的
external,结果打包时直接因为ESM/CJS互操作的语法问题报错; - 本地用Lambda模拟环境测试时,能正常加载模块并运行,只有部署到线上才出问题;
- 确认了
@gsys/properties-node的package.json里type字段是module,确实是ESM模块。
有没有朋友遇到过类似的Serverless+ESM动态加载的问题?或者能帮我分析下可能的原因?感谢大家!
内容来源于stack exchange




