Skip to content

fix(transformer/decorator): transformed decorators should be injected after class-properties has run#12418

Merged
graphite-app[bot] merged 1 commit intomainfrom
07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run
Jul 21, 2025
Merged

fix(transformer/decorator): transformed decorators should be injected after class-properties has run#12418
graphite-app[bot] merged 1 commit intomainfrom
07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run

Conversation

@Dunqing
Copy link
Member

@Dunqing Dunqing commented Jul 21, 2025

Example:

Input:

function myPropertyDecorator() {
  return (target: any, propertyKey: number) => {
    console.log(target.constructor.modelName, propertyKey);
  };
}

class Test {
  public static modelName = 'Test';

  @myPropertyDecorator()
  public myProperty = 1;
}

const instance = new Test();

export default instance;

Output diff:

function myPropertyDecorator() {
        return (target, propertyKey) => {
                console.log(target.constructor.modelName, propertyKey);
        };
}
class Test {
        constructor() {
                babelHelpers.defineProperty(this, "myProperty", 1);
        }
}
-babelHelpers.decorate([myPropertyDecorator()], Test.prototype, "myProperty", void 0);
-babelHelpers.defineProperty(Test, "modelName", "Test");
+babelHelpers.defineProperty(Test, "modelName", "Test");
+babelHelpers.decorate([myPropertyDecorator()], Test.prototype, "myProperty", void 0);

const instance = new Test();
export default instance;

The cause is that we transform class fields and decorators in two different plugins, and the decorator plugin should be transformed first, and then run the class-properties plugin, because class-properties might erase some decorator information. However, this cause transformed decorators injected before transformed class fields.

This PR fixes this problem by collecting all transformed decorators that wait for injection, and injecting them in batch after the class-properties plugin has run.

@github-actions github-actions bot added A-transformer Area - Transformer / Transpiler C-bug Category - Bug labels Jul 21, 2025
Copy link
Member Author

Dunqing commented Jul 21, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@codspeed-hq
Copy link

codspeed-hq bot commented Jul 21, 2025

CodSpeed Instrumentation Performance Report

Merging #12418 will not alter performance

Comparing 07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run (986c48e) with main (6838948)

Summary

✅ 34 untouched benchmarks

@Dunqing Dunqing force-pushed the 07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run branch from 6151a37 to 01c7eb0 Compare July 21, 2025 06:17
@graphite-app graphite-app bot changed the base branch from 07-21-fix_transformer_typescript_should_remove_abstract_field_early to graphite-base/12418 July 21, 2025 06:30
@Dunqing Dunqing marked this pull request as ready for review July 21, 2025 06:37
@Dunqing Dunqing requested a review from overlookmotel as a code owner July 21, 2025 06:37
@Dunqing Dunqing force-pushed the graphite-base/12418 branch from 44a1c97 to 5ebd4b0 Compare July 21, 2025 06:37
@Dunqing Dunqing force-pushed the 07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run branch from 01c7eb0 to 479484a Compare July 21, 2025 06:37
@Dunqing Dunqing changed the base branch from graphite-base/12418 to 07-21-fix_transformer_typescript_should_remove_abstract_field_early July 21, 2025 06:37
@graphite-app graphite-app bot changed the base branch from 07-21-fix_transformer_typescript_should_remove_abstract_field_early to graphite-base/12418 July 21, 2025 06:38
@graphite-app graphite-app bot force-pushed the graphite-base/12418 branch from 5ebd4b0 to c4a2d79 Compare July 21, 2025 06:48
@graphite-app graphite-app bot force-pushed the 07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run branch from 479484a to d7a33a2 Compare July 21, 2025 06:48
@graphite-app graphite-app bot changed the base branch from graphite-base/12418 to main July 21, 2025 06:49
@graphite-app graphite-app bot force-pushed the 07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run branch from d7a33a2 to 0feb179 Compare July 21, 2025 06:49
@overlookmotel
Copy link
Member

overlookmotel commented Jul 21, 2025

@Dunqing I've amended the diff in PR description. Before the diff showed no change. I think what I've changed it to is what you were intending to say, but can you please check my change and confirm?

@Dunqing
Copy link
Member Author

Dunqing commented Jul 21, 2025

@Dunqing I've amended the diff in PR description. Before the diff showed no change. I think what I've changed it to is what you were intending to say, but can you please check my change and confirm?

Thanks! Yes, it is good now.

Copy link
Member

@overlookmotel overlookmotel left a comment

Choose a reason for hiding this comment

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

It feels a bit hacky to have 2 exit_class methods for the same transform, but I see why it's necessary.

The interaction between TS, decorators, and class props transforms feels a bit fragile, but I'm not sure how to improve it...

@overlookmotel overlookmotel added the 0-merge Merge with Graphite Merge Queue label Jul 21, 2025
Copy link
Member

overlookmotel commented Jul 21, 2025

Merge activity

… after class-properties has run (#12418)

* close rolldown/rolldown#5374

Example:

Input:
```ts
function myPropertyDecorator() {
  return (target: any, propertyKey: number) => {
    console.log(target.constructor.modelName, propertyKey);
  };
}

class Test {
  public static modelName = 'Test';

  @myPropertyDecorator()
  public myProperty = 1;
}

const instance = new Test();

export default instance;
```

Output diff:
```diff
function myPropertyDecorator() {
        return (target, propertyKey) => {
                console.log(target.constructor.modelName, propertyKey);
        };
}
class Test {
        constructor() {
                babelHelpers.defineProperty(this, "myProperty", 1);
        }
}
-babelHelpers.decorate([myPropertyDecorator()], Test.prototype, "myProperty", void 0);
-babelHelpers.defineProperty(Test, "modelName", "Test");
+babelHelpers.defineProperty(Test, "modelName", "Test");
+babelHelpers.decorate([myPropertyDecorator()], Test.prototype, "myProperty", void 0);

const instance = new Test();
export default instance;
```

The cause is that we transform class fields and decorators in two different plugins, and the decorator plugin should be transformed first, and then run the class-properties plugin, because class-properties might erase some decorator information. However, this cause transformed decorators injected before transformed class fields.

This PR fixes this problem by collecting all transformed decorators that wait for injection, and injecting them in batch after the `class-properties` plugin has run.
@graphite-app graphite-app bot force-pushed the 07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run branch from 0feb179 to 986c48e Compare July 21, 2025 15:14
@graphite-app graphite-app bot merged commit 986c48e into main Jul 21, 2025
25 checks passed
@graphite-app graphite-app bot removed the 0-merge Merge with Graphite Merge Queue label Jul 21, 2025
@graphite-app graphite-app bot deleted the 07-21-fix_transformer_decorator_transformed_decorators_should_be_injected_after_class-properties_has_run branch July 21, 2025 15:22
@Dunqing
Copy link
Member Author

Dunqing commented Jul 22, 2025

It feels a bit hacky to have 2 exit_class methods for the same transform, but I see why it's necessary.

The interaction between TS, decorators, and class props transforms feels a bit fragile, but I'm not sure how to improve it...

Yes, I am either. Dealing with decorators around these three plugins is quite error-prone. I would consider your suggestion to add more tests that enable these three plugins.

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

Labels

A-transformer Area - Transformer / Transpiler C-bug Category - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Incorrect ordering of static class members and decorators

2 participants