Skip to content

fix(optimizer): wrap classes whose Angular definitions are static members or static blocks - #604

Merged
Brooooooklyn merged 2 commits into
voidzero-dev:mainfrom
encharm:fix/wrap-class-static-blocks
Oct 23, 2026
Merged

Brooooooklyn merged 2 commits into
voidzero-dev:mainfrom
encharm:fix/wrap-class-static-blocks

Conversation

@Rush

@Rush Rush commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

The linker writes a library's partial declarations as static blocks inside the class:

class TabsComponent {
  static { this.ɵfac = i0.ɵɵngDeclareFactory(...) }
  static { this.ɵcmp = i0.ɵɵngDeclareComponent(...) }
}

AdjustStaticMembers only wrapped classes followed by downleveled assignments (TabsComponent.ɵcmp = …), so no linked class was wrapped in a pure IIFE and the bundler kept every class of a library it couldn't prove unused. In an app importing one component of @deepkit/desktop-ui that was ~1 MB of the library instead of ~150 KB.

Like the Angular CLI's adjust-static-class-members Babel plugin, a class is now also wrapped when its static properties and single-assignment static blocks hold Angular definitions, values marked /* @__PURE__ */, or pure expressions. A later assignment of an impure value to the class still prevents wrapping.

Two related fixes in the same pass:

  • class declarations are found from the AST rather than by looking for = in their text, where an = anywhere in the body stopped any wrapping; export class is handled too;
  • the wrapper is inserted around the class instead of replacing its text, so the other passes' edits inside the class stay valid.

New tests in optimizer/adjust_static_members.rs: wraps_class_with_angular_definitions_in_static_blocks, wraps_exported_class_declarations, wraps_pure_static_values, marked_pure_static_values_wrap, wraps_class_declaration_with_assignments_inside, keeps_static_members_with_side_effects, plain_classes_stay.

Rush added 2 commits October 21, 2026 17:58
…bers or static blocks

The linker writes partial declarations as `static { this.ɵcmp = … }` blocks inside the class, but
AdjustStaticMembers only wrapped classes followed by downleveled assignments (`X.ɵcmp = …`), so no linked
class was wrapped and esbuild kept every class of a library: importing one component of @deepkit/desktop-ui
bundled ~1 MB instead of ~150 KB. Like the Angular CLI's adjust-static-class-members, a class is now also
wrapped when its static properties and single-assignment static blocks hold Angular definitions, values
marked pure, or pure expressions; a later assignment of an impure value to the class prevents wrapping.

Class declarations are found from the AST rather than by looking for `=` in their text (an `=` in the
body stopped any wrapping), `export class` is handled, and the wrapper is inserted around the class instead
of replacing it, so other passes' edits inside it stay valid.
@Brooooooklyn

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T05:43:18.496516Z 5c07079 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c070791ec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/oxc_angular_compiler/src/optimizer/adjust_static_members.rs
Comment thread crates/oxc_angular_compiler/src/optimizer/adjust_static_members.rs
Comment thread crates/oxc_angular_compiler/src/optimizer/adjust_static_members.rs
Comment thread crates/oxc_angular_compiler/src/optimizer/adjust_static_members.rs

@Brooooooklyn Brooooooklyn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! Follow-up PR with fixes for the Codex findings coming right after this merges.

@Brooooooklyn
Brooooooklyn merged commit c7366e1 into voidzero-dev:main Oct 23, 2026
9 checks passed
@Brooooooklyn

Copy link
Copy Markdown
Member

Fixes for the 4 Codex findings are in #619.

Brooooooklyn added a commit that referenced this pull request Oct 23, 2026
…class

A class followed by `A.ɵfac = f` skipped the class body checks, so an
impure superclass, computed key, static field or static block was still
wrapped. #604 made `static x = register()` wrap this way; the others were
already wrapped before it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brooooooklyn added a commit that referenced this pull request Oct 23, 2026
…re wrapper (#619)

* fix(optimizer): keep classes whose definition runs code out of the pure wrapper

A private static field, a static block that is not `this.x = …`, an impure
superclass or computed key, a static accessor initializer and class
decorators all run when the class is defined, so wrapping the class in a
pure IIFE let bundlers drop them. Member reads, logical operators and a
few globals now count as pure, like the Angular CLI's isPure.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(optimizer): accept member reads only in superclasses and computed keys

A member read can run a getter, so a static value such as `registry.value`
keeps the class unwrapped. Superclasses and computed keys such as `i0.Base`
and `Symbol.iterator` still count as pure: the Angular CLI does not check
those at all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(optimizer): check the class body when Angular statics follow the class

A class followed by `A.ɵfac = f` skipped the class body checks, so an
impure superclass, computed key, static field or static block was still
wrapped. #604 made `static x = register()` wrap this way; the others were
already wrapped before it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(optimizer): narrow pure superclasses and keys, and keep decorated members out

A superclass must now be a declared name, and a computed key a literal, a
declared name or `Symbol.<name>`: reading `ns.Base` can run a getter, and
turning an object key into a string can run its `toString`. Decorators on
class members run when the class is defined, so those classes stay too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

2 participants