Skip to content

WIP: feat(bundler): add @eggjs/tegg-bundler for serverless method bundling#5807

Draft
killagu wants to merge 2 commits intonextfrom
feat/tegg-bundler
Draft

WIP: feat(bundler): add @eggjs/tegg-bundler for serverless method bundling#5807
killagu wants to merge 2 commits intonextfrom
feat/tegg-bundler

Conversation

@killagu
Copy link
Contributor

@killagu killagu commented Feb 24, 2026

Summary

WIP — 功能开发中,@utoo/pack 的 platform: 'node' 支持尚在进行中。

  • 新增 @eggjs/tegg-bundler 包,支持按 controller method 粒度打包 serverless bundle
  • 核心组件:MethodAnalyzer(静态分析方法体依赖)、DependencyResolver(传递依赖闭包)、EntryGenerator(生成入口文件)、MetaGenerator(生成路由元数据)、Bundler(编排器)
  • 修复 vitest 异步模块评估导致装饰器文件路径捕获错误的问题(LoaderUtil.loadFile
  • 65 个测试全部通过(57 单元 + 8 e2e)

主要变更

  • 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/packesbuild 依赖

Test plan

  • MethodAnalyzer: 10 tests (basic + private method + edge cases)
  • DependencyResolver: 8 tests (single/multi-module + eventbus skip + edge cases)
  • MetaGenerator: 7 tests (params + deps classification + cross-module)
  • EntryGenerator: 14 tests (content verification + esbuild tree shaking + edge cases)
  • Bundler: 18 tests (orchestration + error paths + config pass-through)
  • E2E: 8 tests (real @utoo/pack builds for simple-app + multi-module-app)

🤖 Generated with Claude Code

killagu and others added 2 commits February 24, 2026 00:40
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>
@cloudflare-workers-and-pages
Copy link

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9cf0e28
Status:🚫  Build failed.

View logs

@cloudflare-workers-and-pages
Copy link

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9cf0e28
Status:🚫  Build failed.

View logs

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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

  • New Serverless Bundler Package: Introduced @eggjs/tegg-bundler, a new package designed to bundle individual controller methods for serverless deployments, optimizing for smaller, more efficient function packages.
  • Core Bundler Components: Developed several core components including MethodAnalyzer for static dependency analysis, DependencyResolver for transitive dependency closure, EntryGenerator for creating minimal entry files, MetaGenerator for generating routing and DI metadata, and Bundler as the orchestrator.
  • Vitest Compatibility Fix: Resolved an issue in LoaderUtil.loadFile where vitest's asynchronous module evaluation could lead to incorrect file path capture for decorated classes, ensuring proper metadata resolution.
  • Comprehensive Test Coverage: Achieved full test coverage with 65 passing tests, comprising 57 unit tests and 8 end-to-end tests, validating the functionality and integration of the new bundler.
Changelog
  • .gitignore
    • Added a new entry to ignore bundler e2e test output.
  • package.json
    • Added a new dependency, @utoo/pack, linked locally.
  • pnpm-lock.yaml
    • Updated several dependency versions, including body-parser, http-errors, langchain related packages, hono, langsmith, qs, and removed axios and proxy-from-env.
    • Added new entries for @utoo/pack, esbuild, ansi-styles, chalk, and @standard-schema/spec.
  • pnpm-workspace.yaml
    • Removed typebox from the catalog and then re-added it.
    • Added @utoo/pack to the catalog and overrides section.
  • tegg/core/bundler/package.json
    • Created a new package definition for @eggjs/tegg-bundler, including dependencies, dev dependencies, and scripts.
  • tegg/core/bundler/src/Bundler.ts
    • Implemented the core Bundler class to orchestrate method-level bundling for serverless functions.
    • Defined interfaces for BundlerOptions, MethodBundleResult, BuildOptions, and BuildFunc.
    • Included logic for loading modules, building dependency graphs, generating entry files, and invoking a build function.
  • tegg/core/bundler/src/DependencyResolver.ts
    • Implemented the DependencyResolver class to identify direct and transitive dependencies for controller methods based on accessed properties.
  • tegg/core/bundler/src/EntryGenerator.ts
    • Implemented the EntryGenerator class to create minimal TypeScript entry files for bundling individual controller methods, ensuring only necessary dependencies are imported.
  • tegg/core/bundler/src/MetaGenerator.ts
    • Implemented the MetaGenerator class to produce metadata JSON for bundled controller methods, including HTTP routing information and DI dependency details.
  • tegg/core/bundler/src/MethodAnalyzer.ts
    • Implemented the MethodAnalyzer class to statically analyze TypeScript class method bodies, identifying accessed this.xxx properties and following private method calls to determine dependencies.
  • tegg/core/bundler/src/index.ts
    • Exported all public components of the new bundler package, including Bundler, MethodAnalyzer, DependencyResolver, EntryGenerator, and MetaGenerator.
  • tegg/core/bundler/test/Bundler.test.ts
    • Added comprehensive unit tests for the Bundler class, covering method bundle generation, dependency isolation, external configuration passing, and error handling.
  • tegg/core/bundler/test/DependencyResolver.test.ts
    • Added unit tests to verify correct dependency resolution, including scenarios with single and multi-module applications, cross-module dependencies, and graceful skipping of framework built-ins.
  • tegg/core/bundler/test/EntryGenerator.test.ts
    • Added unit tests to confirm the correctness of generated entry files, their import statements, and their tree-shaking behavior when bundled with esbuild.
  • tegg/core/bundler/test/MetaGenerator.test.ts
    • Added unit tests to ensure accurate generation of method metadata, including HTTP routing details, parameter information, and classification of direct versus transitive dependencies.
  • tegg/core/bundler/test/MethodAnalyzer.test.ts
    • Added unit tests for the MethodAnalyzer to validate its ability to detect accessed properties, follow private method calls, and handle edge cases like non-existent methods or files.
  • tegg/core/bundler/test/e2e.test.ts
    • Added end-to-end tests to validate the full bundling pipeline using @utoo/pack, verifying successful builds, loadable bundles, and valid meta JSON files for simple and multi-module applications.
  • tegg/core/bundler/test/fixtures/apps/aop-app/app/module/order/MetricsService.ts
    • Added a new fixture class MetricsService to simulate AOP-like cross-cutting concerns for testing dependency resolution.
  • tegg/core/bundler/test/fixtures/apps/aop-app/app/module/order/OrderController.ts
    • Added a new fixture class OrderController to demonstrate AOP-like service injection and pure methods for testing.
  • tegg/core/bundler/test/fixtures/apps/aop-app/app/module/order/OrderService.ts
    • Added a new fixture class OrderService that injects MetricsService to test transitive dependency chains.
  • tegg/core/bundler/test/fixtures/apps/aop-app/app/module/order/package.json
    • Added a package.json file for the order module fixture.
  • tegg/core/bundler/test/fixtures/apps/eventbus-app/app/module/notify/NotifierService.ts
    • Added a new fixture class NotifierService that injects a framework built-in EventBus (not in GlobalGraph) to test graceful skipping.
  • tegg/core/bundler/test/fixtures/apps/eventbus-app/app/module/notify/NotifyController.ts
    • Added a new fixture class NotifyController that uses NotifierService to test event bus scenarios.
  • tegg/core/bundler/test/fixtures/apps/eventbus-app/app/module/notify/UserService.ts
    • Added a new fixture class UserService for the event bus application fixture.
  • tegg/core/bundler/test/fixtures/apps/eventbus-app/app/module/notify/package.json
    • Added a package.json file for the notify module fixture.
  • tegg/core/bundler/test/fixtures/apps/multi-module-app/app/module/bar/BarController.ts
    • Added a new fixture class BarController to test cross-module injections and HTTP parameter decorators.
  • tegg/core/bundler/test/fixtures/apps/multi-module-app/app/module/bar/package.json
    • Added a package.json file for the bar module fixture.
  • tegg/core/bundler/test/fixtures/apps/multi-module-app/app/module/foo/FooRepository.ts
    • Added a new fixture class FooRepository as a private dependency within the foo module.
  • tegg/core/bundler/test/fixtures/apps/multi-module-app/app/module/foo/FooService.ts
    • Added a new fixture class FooService with public access, injecting FooRepository to demonstrate transitive dependencies.
  • tegg/core/bundler/test/fixtures/apps/multi-module-app/app/module/foo/package.json
    • Added a package.json file for the foo module fixture.
  • tegg/core/bundler/test/fixtures/apps/private-method-app/app/module/api/ApiController.ts
    • Added a new fixture class ApiController to test dependency analysis through private method calls.
  • tegg/core/bundler/test/fixtures/apps/private-method-app/app/module/api/OrderService.ts
    • Added a new fixture class OrderService for the private method application fixture.
  • tegg/core/bundler/test/fixtures/apps/private-method-app/app/module/api/UserService.ts
    • Added a new fixture class UserService for the private method application fixture.
  • tegg/core/bundler/test/fixtures/apps/private-method-app/app/module/api/package.json
    • Added a package.json file for the api module fixture.
  • tegg/core/bundler/test/fixtures/apps/simple-app/app/module/user/AdminService.ts
    • Added a new fixture class AdminService for the simple application fixture.
  • tegg/core/bundler/test/fixtures/apps/simple-app/app/module/user/UserController.ts
    • Added a new fixture class UserController to demonstrate method-level dependency isolation.
  • tegg/core/bundler/test/fixtures/apps/simple-app/app/module/user/UserService.ts
    • Added a new fixture class UserService for the simple application fixture.
  • tegg/core/bundler/test/fixtures/apps/simple-app/app/module/user/package.json
    • Added a package.json file for the user module fixture.
  • tegg/core/bundler/tsconfig.json
    • Created a new tsconfig.json file for the bundler package, extending the root configuration.
  • tegg/core/bundler/vitest.config.ts
    • Created a new vitest.config.ts file for the bundler package, configuring test includes, excludes, coverage, and environment options.
  • tegg/core/loader/src/LoaderUtil.ts
    • Modified loadFile to correctly set absolute file paths for loaded classes, addressing issues with vitest's async module evaluation and decorator metadata.
  • tsconfig.json
    • Updated the root tsconfig.json to include the new tegg/core/bundler project reference.
Activity
  • The pull request is currently a Work In Progress (WIP), indicating ongoing development.
  • The author, killagu, has implemented a new bundler package to support serverless method bundling.
  • A specific fix was applied to LoaderUtil.ts to resolve issues with file path capture during vitest's async module evaluation.
  • All 65 tests, consisting of 57 unit tests and 8 end-to-end tests, are reported as passing, demonstrating the stability and correctness of the new features.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +5646 to +5648
ansi-styles@5.2.0:
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
engines: {node: '>=10'}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The ansi-styles package has been downgraded from 6.2.3 to 5.2.0. This is a major version downgrade. Please confirm the reason for this change and ensure compatibility with other dependencies that might rely on features or APIs introduced in ansi-styles@6.

Comment on lines +48 to +50
"dependencies": {
"@utoo/pack": "link:../../../../../tnpm/utoo/packages/pack"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The body-parser package has been downgraded from 2.2.2 to 2.2.0. Please ensure this downgrade does not introduce any regressions or known vulnerabilities. It would be beneficial to add a comment explaining the reason for this specific version lock.

http-errors:
specifier: ^2.0.0
version: 2.0.1
version: 2.0.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The http-errors package has been downgraded from 2.0.1 to 2.0.0. Similar to body-parser, please verify that this change is intentional and does not lead to any unexpected behavior or security concerns.

Comment on lines +6700 to 6702
form-data@4.0.4:
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
engines: {node: '>= 6'}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The form-data package has been downgraded from 4.0.5 to 4.0.4. While this is a minor version change, it's good practice to confirm that this specific version is required or preferred, and that it doesn't impact functionality.

Comment on lines +61 to +63
"engines": {
"node": ">=22.18.0"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
if (!accessedProps.has(injectObj.refName as string)) continue;
if (typeof injectObj.refName !== 'string' || !accessedProps.has(injectObj.refName)) continue;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant