WIP: feat(bundler): add @eggjs/tegg-bundler for serverless method bundling#5807
WIP: feat(bundler): add @eggjs/tegg-bundler for serverless method bundling#5807
Conversation
Add a new package that bundles individual HTTP controller methods for serverless deployment. Key components: - MethodAnalyzer: TypeScript AST analysis to detect service accesses per method, including ES private method (#name) call chain following - DependencyResolver: Resolves full transitive dependency closure from the GlobalGraph, gracefully skipping framework built-ins - EntryGenerator: Generates minimal entry files with relative imports for tree-shaking-friendly bundling - MetaGenerator: Produces HTTP routing and DI dependency metadata - Bundler: Orchestrates the full pipeline with pluggable BuildFunc Includes 40 tests across 5 test files with 4 fixture apps covering cross-module deps, private method chains, framework built-in skipping, AOP-like transitive deps, and real esbuild bundle verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…test coverage - Add e2e tests with real @utoo/pack builds (simple-app + multi-module-app) - Fix vitest file path issue in LoaderUtil (async module evaluators cause wrong stack frames in decorator file path capture) - Add Bundler error path tests (no JS output, buildFunc errors, empty modules) - Add Bundler config tests (default externals, mode, tsconfig generation) - Add EntryGenerator edge case tests (dedup, non-class skip, throw on missing path) - Add MethodAnalyzer edge cases (non-existent file, program cache) - Add MetaGenerator dependency classification tests (direct vs transitive) - Add DependencyResolver edge case (unmatched accessedProps) - Support per-method output dirs, CJS compat, experimentalDecorators tsconfig Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello @killagu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new bundler package aimed at optimizing serverless deployments by creating highly granular bundles for individual controller methods. It intelligently analyzes method dependencies and generates minimal, self-contained packages, significantly reducing deployment size and improving cold start times for serverless functions. This enhancement streamlines the development and deployment workflow for serverless applications built with Egg.js. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces the @eggjs/tegg-bundler package for serverless deployments, which bundles individual controller methods into standalone files. It includes changes to .gitignore, package.json, pnpm-lock.yaml, and pnpm-workspace.yaml, along with new files for the bundler's core logic, dependency resolution, entry generation, meta generation, and method analysis. The review comments highlight several concerns, including downgrades of ansi-styles, body-parser, http-errors, form-data, and qs packages, the use of a link: specifier for @utoo/pack, a restrictive engines.node version, a hardcoded target in the build function, unsafe as any cast for dynamic import of @utoo/pack, and potentially unsafe injectObj.refName as string casts.
| ansi-styles@5.2.0: | ||
| resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} | ||
| engines: {node: '>=10'} |
| "dependencies": { | ||
| "@utoo/pack": "link:../../../../../tnpm/utoo/packages/pack" | ||
| }, |
There was a problem hiding this comment.
The dependency @utoo/pack is added using a link: specifier. While this is common in monorepos for local development, it's important to ensure that the production build process correctly resolves this dependency, either by publishing @utoo/pack or by ensuring it's available in the deployment environment.
| body-parser: | ||
| specifier: ^2.0.0 | ||
| version: 2.2.2 | ||
| version: 2.2.0 |
| http-errors: | ||
| specifier: ^2.0.0 | ||
| version: 2.0.1 | ||
| version: 2.0.0 |
| form-data@4.0.4: | ||
| resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} | ||
| engines: {node: '>= 6'} |
| "engines": { | ||
| "node": ">=22.18.0" | ||
| } |
There was a problem hiding this comment.
The engines.node field specifies a very high minimum version (>=22.18.0). While this might be the current development environment, it's quite restrictive. Consider if a slightly broader range (e.g., >=20.0.0) could be supported to increase compatibility, or add a comment explaining why such a specific version is required.
| await fs.writeFile(path.join(methodOutputPath, 'package.json'), '{"type":"commonjs"}\n'); | ||
| await this.buildFunc({ | ||
| entry: [{ name: key, import: entryPath }], | ||
| target: 'node 22', |
There was a problem hiding this comment.
The target: 'node 22' is hardcoded for the build function. It would be more flexible to make this configurable via the BundlerOptions interface, allowing users to specify their desired Node.js target environment for the generated bundles.
| target: 'node 22', | |
| target: options.nodeTarget ?? 'node 22', |
| // Users must install @utoo/pack themselves if using the default build function. | ||
| try { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| const { build } = (await import('@utoo/pack/esm/commands/build.js' as any)) as { |
There was a problem hiding this comment.
The as any cast for the dynamic import of @utoo/pack/esm/commands/build.js suppresses type safety. It would be better to define a proper type for the imported module or the build function to maintain type correctness.
const { build } = (await import('@utoo/pack/esm/commands/build.js')) as { build: (options: { config: BuildOptions }, projectPath?: string, rootPath?: string) => Promise<void>; };|
|
||
| // Seed with directly accessed inject objects from the controller | ||
| for (const injectObj of controllerProto.injectObjects) { | ||
| if (!accessedProps.has(injectObj.refName as string)) continue; |
There was a problem hiding this comment.
The injectObj.refName as string cast is potentially unsafe if refName is not guaranteed to be a string. Consider adding a runtime check or ensuring refName is always typed as a string to prevent potential type errors.
| if (!accessedProps.has(injectObj.refName as string)) continue; | |
| if (typeof injectObj.refName !== 'string' || !accessedProps.has(injectObj.refName)) continue; |
| const dependencies: DependencyMetaInfo[] = []; | ||
|
|
||
| for (const injectObj of controllerProto.injectObjects) { | ||
| if (!accessedProps.has(injectObj.refName as string)) continue; |
There was a problem hiding this comment.
Similar to DependencyResolver.ts, the injectObj.refName as string cast here could be unsafe. It's best to ensure refName is always a string or handle other types explicitly to maintain type safety.
| if (!accessedProps.has(injectObj.refName as string)) continue; | |
| if (typeof injectObj.refName !== 'string' || !accessedProps.has(injectObj.refName)) continue; |
Summary
@eggjs/tegg-bundler包,支持按 controller method 粒度打包 serverless bundleMethodAnalyzer(静态分析方法体依赖)、DependencyResolver(传递依赖闭包)、EntryGenerator(生成入口文件)、MetaGenerator(生成路由元数据)、Bundler(编排器)LoaderUtil.loadFile)主要变更
tegg/core/bundler/— 新包,包含 5 个核心模块 + 5 个 fixture app + 6 个测试文件tegg/core/loader/src/LoaderUtil.ts— 修复 vitest 下PrototypeUtil.getFilePath返回node:internal/...的问题pnpm-workspace.yaml/package.json— 新增@utoo/pack和esbuild依赖Test plan
🤖 Generated with Claude Code