Commit Graph

892 Commits

Author SHA1 Message Date
sapphi-red c135beb1b5 feat(codegen): keep function expression PIFEs (#12470)
Made function expression PIFEs to be kept. Similar to #12353 but for function expressions.

The minsize for victory is bigger because webpack's output contains many PIFEs (some shouldn't be written as PIFEs though).

refs #12353
2025-07-24 00:49:59 +00:00
overlookmotel 3fa0cff95c test(transform/typescript): improve test case (#12468)
Follow-on after #12456. Improve the test case added in that PR to make sure values are indeed based on const evaluation of the `TemplateLiteral`s, not the enum member names, by making them different from each other.
2025-07-23 10:26:48 +00:00
Dunqing dcb245ce4c fix(transformer/typescript): incorrect evaluation of TemplateLiteral in enum (#12456)
* close https://github.com/rolldown/rolldown/issues/5386
2025-07-23 04:21:13 +00:00
Dunqing 986c48e2a9 fix(transformer/decorator): transformed decorators should be injected after class-properties has run (#12418)
* close https://github.com/rolldown/rolldown/issues/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.
2025-07-21 15:14:41 +00:00
Dunqing c4a2d79252 fix(transformer/typescript): should remove abstract field early (#12417)
* close https://github.com/rolldown/rolldown/issues/5326

Regression introduced by #12212. The transform execution shift causes the bug because the `class-properties` plugin always assumes that all fields accessed can be transformed. However, the prior change breaks the assumption.

This PR reverts some changes to remove the ts-only field early, which respects that `class-properties` assumption
2025-07-21 06:40:24 +00:00
sapphi-red 0920e9827e feat(codegen): keep arrow function PIFEs (#12353)
PIFE is the abbreviation of "Possibly-Invoked Function Expressions". It is a function expression wrapped with a parenthesized expression.
PIFEs annotate functions that are likely to be invoked eagerly. When v8 encounters such expressions, it compiles them eagerly (rather than compiling it later). See [v8's blog post](https://v8.dev/blog/preparser#pife) for more details.

The blog post only mentions regular FunctionExpressions, but ArrowFunctions are also supported. The cases that will be eagerly compiled are:

- when `!` comes before function literals ([code](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L3730-L3733)) (e.g. `!function(){}`)
- when a function expression is wrapped with parenthesis ([code](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L2243-L2248)) (e.g. `(function () {})`, `(async function () {})`)
- when an arrow function is wrapped with parenthesis ([code1](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L2173-L2174), [code2](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L2232-L2259), [code3](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L3297-L3298)) (e.g. `console.log((() => {}))`)
- when `()` or ``` `` ``` (tagged templates) comes after function literals (only in some cases) ([code1](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L3987-L3992), [code2](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L4344-L4348)) (e.g. `~function(){}()`, ```~function(){}`` ```)
- when [explicit compile hints](https://v8.dev/blog/explicit-compile-hints) are used ([code1](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser.cc#L2717-L2720), [code2](https://github.com/v8/v8/blob/328f6c467b940f322544567740c9c871064d045c/src/parsing/parser-base.h#L5070-L5073)) (e.g. `//# allFunctionsCalledOnLoad`)

Keeping PIFEs as-is in the code generation will unlock optimizations like https://github.com/rolldown/rolldown/pull/5319.

_Note: this PR only implements arrow function PIFEs for now._
2025-07-20 13:13:12 +00:00
renovate[bot] 8190f3a85f chore(deps): lock file maintenance npm packages (#12254) 2025-07-15 11:49:48 +08:00
Jason Ho c7b1056f16 fix(transformer/object-rest-spread): arrow function expressions with object rest patterns (#12185)
close: https://github.com/oxc-project/oxc/issues/12184

I couldn't figure out where **exactly** to put tests regarding object
rest spread and arrow function transformation.

Probably somewhere underneath tasks/transform_conformance
2025-07-15 08:54:55 +08:00
Boshen 9b14fbcd4f feat(ast): add ThisExpression to TSTypeName (#12156)
part of #12042

Aligned with estree: https://github.com/typescript-eslint/typescript-eslint/blob/5c5e3d5c3853ab613e06be0d77a40e970017b3fc/packages/ast-spec/src/unions/EntityName.ts#L5
2025-07-13 01:43:57 +00:00
Dunqing 23c5951d26 fix(transformer/legacy-decorator): decorated fields with the declare modifier are not transformed (#12212)
Input: 
```ts
class DeclareFields {
  @dec declare name: string;
  @dec declare output: number;
}
```

Before output:
```ts
class DeclareFields {}
```

After output:
```ts
class DeclareFields {}

babelHelpers.decorate([dec], DeclareFields.prototype, "name", void 0);
babelHelpers.decorate([dec], DeclareFields.prototype, "output", void 0);
```


The `declare` fields with decorators should be transformed, but they
would be removed in the typescript plugin before legacy decorators
transform them, so this PR is to adjust the transform order and allow
`declare` fields can be transformed
2025-07-12 09:52:13 +08:00
Dunqing e93e4c84b5 fix(transformer/styled-components): template literal expressions order is wrong after minification (#12211)
Related: https://github.com/oxc-project/oxc/pull/12202

`swap_remove` will swap the last element to the index you want to remove, so this will cause the expression's order to be incorrect. Here is order sensitive, so we can't use `swap_remove`.
2025-07-11 09:47:35 +00:00
overlookmotel 47ff2bb5be test(transformer/styled-components): add failing test case for comment removal (#12202)
Failing test case which demonstrates a bug with comments removal from CSS. The last 2 expressions are in wrong order.

Problem is likely `swap_remove`, which alters order of the `Vec`'s elements:

https://github.com/oxc-project/oxc/blob/86eb10882eeacbe05a0dc5e0e3adfad9a9890821/crates/oxc_transformer/src/plugins/styled_components.rs#L407-L412
2025-07-11 09:08:42 +00:00
Dunqing bfd47f89b5 fix(transformer/styled-components): should use source path to generate file hash when the path is absoulte path (#12199)
related: https://github.com/oxc-project/oxc/issues/11876#issuecomment-3058036852
2025-07-11 04:54:35 +00:00
overlookmotel 7b1f4a51bc test(transformer/styled-components): reformat test case output (#12201)
Just reformat the output so it's easier to read. Does not change the test itself.
2025-07-10 17:35:54 +00:00
Dunqing 19b97c06a3 feat(transformer): support styled components plugin (#12066)
close: #11876

This plugin adds comprehensive support for styled-components with server-side rendering, style minification, and enhanced debugging capabilities. The implementation is ported from the [official Babel plugin](https://github.com/styled-components/babel-plugin-styled-components) to ensure compatibility and feature parity.

## Current Limitations

⚠️ **Import Support**: This plugin currently only supports styled-components imported via ES modules (`import` statements). CommonJS imports using `require("styled-components")` are not yet supported.

## Feature Support

### Options:
 **✅ Fully Supported:**
 - `displayName`: Adds display names for debugging
 - `fileName`: Controls filename prefixing in display names
 - `ssr`: Adds unique component IDs for server-side rendering
 - `transpileTemplateLiterals`: Converts template literals to function calls
 - `minify`: Minifies CSS content in template literals
 - `namespace`: Adds namespace prefixes to component IDs
 - `meaninglessFileNames`: Controls which filenames are considered meaningless

 **⚠️ Partially Supported:**
 - `pure`: Only supports call expressions, not tagged template expressions (bundler limitation)

 **❌ Not Yet Implemented:**
 - `cssProp`: JSX css prop transformation
 - `topLevelImportPaths`: Custom import path handling

## Testing

The test suite is adapted from the [official Babel plugin tests](https://github.com/styled-components/babel-plugin-styled-components) with modifications to integrate with our testing infrastructure.

**Note**: Some test outputs differ from the original due to our different hashing algorithm for component IDs. This is intentional and follows the same approach used by SWC's implementation.
2025-07-10 00:39:52 +00:00
Boshen 8b30a5bbd6 feat(codegen)!: introduce CommentOptions (#12114)
Control printing of comments more precisely via more options:

The logic change here is that jsdoc is no longer part of annotation
comments.

```rust
pub struct CommentOptions {
    /// Print normal comments that do not have special meanings.
    ///
    /// At present only statement level comments are printed.
    ///
    /// Default is `true`.
    pub normal: bool,

    /// Print jsdoc comments.
    ///
    /// * jsdoc: `/** jsdoc */`
    ///
    /// Default is `true`.
    pub jsdoc: bool,

    /// Print annotation comments.
    ///
    /// * pure: `/* #__PURE__ */` and `/* #__NO_SIDE_EFFECTS__ */`
    /// * webpack: `/* webpackChunkName */`
    /// * vite: `/* @vite-ignore */`
    /// * coverage: `v8 ignore`, `c8 ignore`, `node:coverage`, `istanbul ignore`
    ///
    /// Default is `true`.
    pub annotation: bool,

    /// Print legal comments.
    ///
    /// * starts with `//!` or `/*!`.
    /// * contains `/* @license */` or `/* @preserve */`
    ///
    /// Default is [LegalComment::Inline].
    pub legal: LegalComment,
}
```

closes #12030
related https://github.com/rolldown/tsdown/issues/357
2025-07-07 08:57:59 +00:00
renovate[bot] 7f87dcab46 chore(deps): lock file maintenance npm packages (#11972) 2025-06-30 14:10:13 +08:00
Dunqing e36ca9a845 feat(transformer/react-refresh): support checking a variable whether is used in JSX elements or JSX-like calls (#11859)
Fixes: https://github.com/oxc-project/oxc/issues/11824

We have to introduce a visitor to pre-collect all bindings that are used in JSX elements or JSX-like calls, because a variable can be referenced before or after the declaration.
2025-06-25 07:35:22 +00:00
renovate[bot] 15d84be165 chore(deps): lock file maintenance npm packages (#11850) 2025-06-23 13:02:38 +08:00
overlookmotel 8627cedaeb fix(ast): ComputedMemberExpression::static_property_name use cooked for TemplateElements (#11829)
Fix `ComputedMemberExpression::static_property_name`. The value of a
template literal is the cooked version. e.g. ``obj[`\x41`]`` -> property
name `"A"` not `"\x41"`.

The only place we use this method is in React transform. Add a test for
it there.
2025-06-23 12:47:25 +08:00
renovate[bot] 9525250ccb chore(deps): lock file maintenance npm packages (#11731) 2025-06-16 10:14:59 +08:00
Boshen e4804bafaf fix(parser): parse decorator on abstract class (#11625) 2025-06-12 04:53:10 +00:00
Boshen e9a8832ce6 refactor(parser): rewrite decorator parsing (#11604)
fixes #11593

Parse decorators and pass them around explicitly instead of using a state to store them.
2025-06-11 11:11:58 +00:00
renovate[bot] 888b9c8d43 chore(deps): lock file maintenance npm packages (#11557) 2025-06-09 14:34:46 +08:00
Boshen cd063d1ae5 feat(parser): produce syntax error for decorators on overload (#11496)
fixes #11029

```ts
class A {
    @decorator()
    method(): Array<string>
}
```
2025-06-05 15:02:36 +00:00
renovate[bot] 9e2686a62b chore(deps): lock file maintenance npm packages (#11289) 2025-05-26 11:55:59 +08:00
Dunqing 907e114400 fix(transformer/optional-chaining): optional chaining expression isn't being transformed correctly when inside a double call expression (#11179)
close: https://github.com/oxc-project/oxc/issues/11141
2025-05-20 02:07:40 +00:00
Boshen 13825c03bc chore(coverage): bump test262, babel and typescript versions (#11163) 2025-05-20 01:51:51 +00:00
Dunqing fcd5ba9a21 fix(transformer/legacy-decorator): decorates insertion order is incorrect (#11144)
Insert the decorations early rather than waiting for the `class` assignment to insert them together. This is to avoid the decorators being inserted before the `defineProperty(xxx)` which could cause accessing an field still doesn't decorated yet.

Input:

```ts
function Bar(): ClassDecorator {
  return (_target) => {
    console.log(Bar.name)
  }
}

@Bar()
class Foo {
  static foo = `${Foo.name}`;
}
```

Before output:
```js
var _Foo;
function Bar() {
  return (_target) => {
    console.log(Bar.name);
  };
}
let Foo = _Foo = class Foo {};
babelHelpers.defineProperty(Foo, "foo", `${_Foo.name}`);
Foo = _Foo = babelHelpers.decorate([Bar()], Foo);
```

After output:
```js
var _Foo;
function Bar() {
  return (_target) => {
    console.log(Bar.name);
  };
}
let Foo = _Foo = class Foo {};
Foo = _Foo = babelHelpers.decorate([Bar()], Foo);
babelHelpers.defineProperty(Foo, "foo", `${_Foo.name}`);
```
2025-05-20 01:45:09 +00:00
Dunqing 468ddf4c08 fix(transformer/legacy-decorator): static class fields cannot be accessed in static field initializer when class-properties plugin is not enabled (#11143)
close: https://github.com/oxc-project/oxc/issues/11111

This is the missing part we were ported from [TypeScript](https://github.com/microsoft/TypeScript/blob/b86ab7dbe0eb2f1c9a624486d72590d638495c97/src/compiler/transformers/legacyDecorators.ts#L345-L366).
2025-05-19 13:54:41 +00:00
Dunqing d37449048d fix(transformer/typescript): panics when setPublicClassFields = true and the class includes private field (#11089)
close: #11084

We don't need to transform the private field, which is part of class-properties work, so `unreachable` is expected. The bug is that we haven't skipped it.
2025-05-18 05:10:20 +00:00
renovate[bot] f524552e06 chore(deps): update dependency rust to v1.87.0 (#11063) 2025-05-16 12:16:07 +08:00
Boshen 4e5c73b87c fix(span)!: SourceType::from_path(".js") return js instead of jsx (#11038)
closes #11020

Previously `SourceType::from_path(".js")` treats a `.js` file as `.jsx`
to maximize parse rate for the linter.

Force the `.jsx` change in the linter instead of a generic solution for
`SourceType::from_path`.
2025-05-15 16:58:00 +08:00
Boshen 751876bf6a refactor(parser): rewrite parse class element (#11035)
closes #10942
closes #11010
2025-05-14 14:44:21 +00:00
Boshen 1673ffb05a feat(codegen): rework printing normal / legal / annotation comments (#10997)
fixes #10948

This PR changes how comment options are interpreted.

`CodegenOptions::comments`, `CodegenOptions::annotation_comments` and
`CodegenOptions::legal_comments` are now independent of each other.

### `CodegenOptions::comments`

Controls printing of normal comments. At present, only some leading
comments are preserved. It does not control legal and annotation
comments.


### `CodegenOptions::annotation_comments`

Controls printing of annotation comments

* jsdoc: `/** jsdoc */`                                                 
* pure: `/* #__PURE__ */` and `/* #__NO_SIDE_EFFECTS__ */`              
* webpack: `/* webpackChunkName */`                                     
* vite: `/* @vite-ignore */`                                            
* coverage: `v8 ignore`, `c8 ignore`, `node:coverage`, `istanbul ignore`

### `CodegenOptions::legal_comments`

Controls printing of legal comments

* starts with `//!` or `/*!`.                   
* contains `/* @license */` or `/* @preserve */`

---

Comment generation is also changed:

codegen first builds a comments map by keeping only the needed comments.
Printing functions will then print out comments in their respective
positions.

---

This PR reveals some incorrect comment interpretation:  

* https://github.com/oxc-project/oxc/issues/10998
2025-05-13 19:24:42 +08:00
overlookmotel 27a21a7d05 fix(transformer/jsx): fix parsing JSX pragma comments (#10983)
Fixes #10955.

Allow multiple JSX pragmas in a single comment, or even in the same line. e.g.

```js
/* @jsx h @jsxRuntime classic */

// @jsx h @jsxRuntime classic

/**
  * @jsx h
  * @jsxRuntime classic
  */
```

Also, allow pragmas to be anywhere in the comment, including surrounded by unrelated text.

```js
// Unrelated text @jsx h Unrelated text @jsxRuntime classic

/**
  * Unrelated text.
  * @jsx h
  * Unrelated text.
  * @jsxRuntime classic
  */
```

This is more liberal than Babel, which [has a test](https://github.com/babel/babel/blob/226318d54eaff275500b2723c46ee608c0632df0/packages/babel-plugin-transform-react-jsx/test/fixtures/react/should-not-allow-jsx-pragma-to-be-anywhere-in-comment/input.js) specifically to outlaw placing pragmas amongst other text in a comment - this test is disabled in this PR.

Instead, we're aligning with ESBuild ([ESBuild playground](https://esbuild.github.io/try/#dAAwLjI1LjQALS1sb2FkZXI9anN4IC0tanN4PWF1dG9tYXRpYwAvLyBAanN4IFNvbWV0aGluZyBibGFoIGJsYWggQGpzeFJ1bnRpbWUgY2xhc3NpYwo8YmxhaC8+Cg)) which seems to allow a `@jsx` pragma anywhere in the text of the comment.
2025-05-13 00:29:27 +00:00
magic-akari 6c20277c4a fix(transformer): Always update jsx options from comments (#10966)
> [!NOTE]  
> The explanation in the comment is incorrect because the `h` function
might be locally defined rather than imported. Removing the conditional
check will fix the issue.

- Closes: #10956

Co-authored-by: Dunqing <dengqing0821@gmail.com>
2025-05-12 23:57:48 +08:00
Dunqing a39eb85db5 fix(transformer/legacy-decorator): abstract class doesn't work in metadata (#10952)
close: #10926
2025-05-12 13:12:00 +00:00
renovate[bot] ed983f895d chore(deps): lock file maintenance npm packages (#10945) 2025-05-12 17:57:45 +08:00
Dunqing 6e47699230 fix(semantic, syntax): TS value module cannot be referenced as type (#10918)
* close https://github.com/oxc-project/oxc/issues/10729
2025-05-09 14:39:49 +00:00
Dunqing faf0a95bb6 refactor(syntax): rename NameSpaceModule to NamespaceModule (#10917)
As much as possible, keep the name the same as `TypeScript`

https://github.com/microsoft/TypeScript/blob/8c62e08448e0ec76203bd519dd39608dbcb31705/src/compiler/types.ts#L5915
2025-05-09 14:24:24 +00:00
camc314 a033c1ec91 fix(transformer/typescript): panic when using removeClassFieldsWithoutInitializer and private class members (#10838) 2025-05-09 10:07:58 +00:00
Dunqing 6de0bc29bd feat(transformer/typescript): support transforming class fields into this assignment (#10854)
Close https://github.com/oxc-project/oxc/issues/10649
2025-05-09 08:43:13 +00:00
Boshen 539eb9d82e feat(parser): accessor modifier cannot be used with readonly and declare modifier. (#10870)
closes #10837

These are syntax errors:

```ts
class Foo {
    readonly accessor a: any;
    declare accessor b: any;
}
```
2025-05-08 08:40:19 +00:00
Boshen 2c05fa1753 fix(parser): fix rhs precedence while parsing PrivateInExpression (#10866)
fixes #10839

```
class Foo {
  #name: string;
  f() { #name in other && this.#name === other.#name }
                 ^^^^^ rhs is only this part
}

RelationalExpression[In, Yield, Await] :
  [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]
```
2025-05-07 14:44:35 +00:00
overlookmotel 79e462dac1 perf(transformer): faster UID generation (#10759)
Alter the algorithm for generating UIDs, to improve its performance.

Previously we stored all existing symbol names which start with `_` in an `FxHashSet`. When creating a UID based on `foo`, it would first see if `_foo` is available, then try `_foo2`, then `_foo3`, then `_foo4` etc.

When creating a lot of UIDs from the same base name (`foo` in example above), this has poor performance as each attempt requires a separate hashset lookup.

Instead, use an `FxHashMap` mapping base name to:

1. Largest number of underscores at start.
2. Largest numeric prefix on end.

i.e. if there are existing symbols `_foo`, `_bar3`, `_bar8`, `_qux`, `___qux2`, this produces a map:

```
{
    "foo": { postfix: 1, underscore_count: 1 },
    "bar": { postfix: 8, underscore_count: 1 },
    "qux": { postfix: 2, underscore_count: 3 },
}
```

When generating a UID, `postfix` is incremented and UID is constructed as `<underscores><base><postfix>`.

This requires a maximum of 1 x hashmap lookup and 1 x hashmap insert for each UID. It also reduces the size of the hashmap where many UIDs are created for same base name.

This new algorithm produces almost same UIDs as Babel, but not quite. 3 tests require their outputs to be changed due to UIDs being different.
2025-05-02 01:15:47 +00:00
Boshen 315143ae42 refactor(codegen)!: remove useless CodeGenerator type alias (#10702) 2025-04-29 17:01:07 +00:00
Dunqing bb522fa0e8 fix(transformer/legacy-decorator): should fallback to Object when a type reference refers to a type symbol (#10633)
Align TypeScript

https://www.typescriptlang.org/play/?experimentalDecorators=true&emitDecoratorMetadata=true&target=99#code/JYWwDg9gTgLgBAbzgMQhANHAQgQypmATzAFM4AtNOAXzgDMoIQ4AiECAExYG4AoXjiQDGAGzxk6AVwB2QmMAjS4goQApecODDwBzEjABccHNMLoNcANYlCRgM4wowaTvObBdoU7AxoRgAqMpLCEACIknt6++LwAlEYAbhDAHHy8ojh2dnAAwiLZCBZCig5QknLQqgACKnBgeDggRqgYdQ0gAEzNVAA+2HiY9VCNAMxGlK1DjQAs42gAdBCLsYgWmsXSdhAiJPMiEDqqUyCD7R2nwyAjFzOxfJrUvI9AA
2025-04-27 11:36:12 +00:00
Dunqing 6abc2f359e fix(transformer/legacy-decorator): keep imports when it's referenced as metadata (#10632)
Fixes https://github.com/oxc-project/oxc-node/pull/112#issuecomment-2829365637

We cannot remove `imports` when they are referenced as metadata, because `reflect-metadata` would call them in runtime.
2025-04-27 11:15:12 +00:00
Dunqing fae8c268b2 feat(transformer/typescript): support allowDeclareFields: false for backward compatibility (#10585)
Although `allow_declare_fields` is deprecated, we still need to support it for backward compatibility.
2025-04-25 01:30:22 +00:00