Commit Graph

53 Commits

Author SHA1 Message Date
Dunqing 65da89795a refactor(napi): share JavaScript RegExp conversion (#25834)
Oxc and Rolldown currently duplicate the N-API conversion between a JavaScript `RegExp` and owned Rust data.

This moves that conversion to `oxc_napi::JsRegExp`. It copies `source`, `flags`, and `lastIndex`, supports converting back to a JavaScript `RegExp`, and keeps regex compilation in each consumer. `oxc_minify_napi::JsRegExp` remains available as a re-export.

This is a good place to put since Rolldown already used `oxc_napi` and crate is exactly for napi related struct
2026-08-18 07:25:04 +00:00
Dunqing 241c559b83 fix(napi/minify): accept RegExp for property filters (#25827)
This follows up on #24741 and addresses API feedback received after it was merged.

### Feature

`mangleProps.include` and `mangleProps.exclude` now accept JavaScript `RegExp` values instead of strings.

The binding copies the regular expression source and flags before async minification starts, then compiles the pattern once with Rust's regex engine. Native and WASI bindings expose the same API. The core Rust minifier API is unchanged.

### Example

```js
mangleProps: {
  include: /^_/,
  exclude: /^__public/,
}
```

### Limitation

Matching uses Rust regex syntax. The `i`, `m`, `s`, and `u` flags are supported. Unsupported JavaScript syntax or flags are returned as option errors.

### AI assistance

OpenAI Codex was used for research, implementation, review, and testing. I reviewed and take responsibility for the changes.
2026-08-18 05:37:31 +00:00
Dunqing a169e4adf8 feat(napi/minify): expose property name mangling options (#24741)
The property mangler added in #24740 is not available through `oxc-minify`. This adds `mangleProps` for configuring it and `mangleCache` for reusing chosen property names.

### Feature

- `include` selects property names to mangle. `exclude` and `reserved` keep names unchanged.
- `quoted` also mangles quoted property names. `debug` produces readable output names.
- `cache` fixes a property to a chosen name. A `false` value keeps the original name.
- Property mangling works in both sync and async APIs. It is separate from identifier mangling.
- When property mangling is enabled and the call succeeds, the result includes the updated `mangleCache`.
- Invalid `mangleProps` regexes and cache entries are returned in `result.errors`.

### Design

The binding converts JavaScript values into core minifier options and results. The core minifier still chooses names and rewrites properties, so both APIs follow the same rules.

A parser error may leave an incomplete AST. In that case, property mangling is skipped and no partial cache is returned.

### Example

```js
const result = minifySync(
  "test.js",
  "let local; obj._often; obj._rare; obj._often;",
  {
    compress: false,
    mangle: false,
    mangleProps: { include: "^_" },
  },
);

result.code; // "let local;obj.e;obj.t;obj.e;"
result.mangleCache; // { _often: "e", _rare: "t" }
```

### Limitation

- `include` and `exclude` use Rust regex syntax. They match anywhere unless the pattern includes anchors.
- Do not reuse the cache on already-mangled output. A mapping is not always safe to apply twice.
- The cache only remembers recorded mappings. It cannot find unchanged names in another input or in external code. Reserve, exclude, or pin those names before the first call.
- String cache targets must be valid JavaScript `IdentifierName` values and cannot be `__proto__`, `constructor`, or `prototype`. The original name `__proto__` cannot be a cache key.

AI assistance: OpenAI Codex and Claude were used for research, implementation, adversarial review, and verification. The contributor remains responsible for reviewing, understanding, and submitting the changes under the repository AI usage policy.
2026-08-18 02:40:36 +00:00
Dunqing 2f5cdb1231 feat(minifier): add property name mangling (#24740)
Oxc can shorten variable names and private class names, but it cannot shorten property names such as `obj._field`. This change adds optional property mangling. It replaces #23772 and gives bundlers a way to use the same property names across several files.

### Feature

- `include` selects names to mangle. `exclude` and `reserved` keep names unchanged.
- The cache can give a property a fixed output name or keep it unchanged.
- The mangler supports member access, object and class keys, destructuring, optional chains, the left side of `in`, and JSX property names.
- `mangle_quoted` also handles strings and template literals used directly as property keys. `/* @__KEY__ */` and `/* #__KEY__ */` can mark one other string or template literal as a property key.
- Short names are assigned in a stable order. More common properties get shorter names. Existing and reserved names are avoided.
- Bundlers can collect names from several programs, create one shared map, and apply it to every program.
- Numeric keys, `__proto__`, `constructor`, `prototype`, TypeScript-only keys, and private names are never changed by this pass.

### Design

Property names are part of JavaScript syntax, not local scope. For this reason, the code lives in `oxc_minifier`. It only reuses the short-name generator from `oxc_mangler`.

For one program, the order is:

```text
collect names → assign names → rewrite once → compress → mangle variables and private names
```

Property names are rewritten before compression because compression can remove information about quotes and comments.

For several programs, a bundler collects names from each program, merges the results, assigns one shared map, and rewrites each program once. Collection can run in parallel. All programs must use the same options.

The shared map keeps property names consistent inside one build. The cache has a different job: it keeps fixed names and reservations across separate minify calls.

### Example

With `include: "^_"` and `mangle_quoted: true`:

```js
obj[`_field`] = 1;
obj._field = 2;
```

becomes:

```js
obj[`e`] = 1;
obj.e = 2;
```

### Limitation

- Property mangling is unsafe when other code still uses the old property name. Public APIs and names shared with external code, host APIs, imported namespace objects, or separate builds must be excluded or reserved.
- Quote handling is per use. Without `mangle_quoted`, `obj._field` may change while `obj["_field"]` does not.
- The mangler cannot find names built at runtime or stored in arbitrary strings. Code that uses `eval`, `Function`, or `with` must reserve any affected names.
- Compression may create a matching property after this pass has finished. For example, `'f' + 'oo_'` may later become `foo_` and will not be mangled.
- TypeScript enum reverse mappings are not supported because the transform creates quoted strings.
- Multi-program support is a caller API. The caller must collect every program that shares mangled properties before assigning names.

The public `oxc-minify` API is stacked in #24741. The downstream multi-chunk integration is shown in rolldown/rolldown#10374.

Supersedes #23772.

AI assistance: OpenAI Codex and Claude were used for research, implementation, review, and testing. The contributor is responsible for reviewing and understanding the changes before submission.
2026-08-17 07:50:51 +00:00
Dunqing 4eb074e69d feat(mangler): add reserved option for names that must not be mangled (#24041)
Adds `MangleOptions::reserved` — equivalent to terser / uglify-js / swc `mangle.reserved`: names in the set are never used as mangled names, and bindings that already carry them keep them. Default empty; default output is byte-identical to main (minsize snapshot unchanged).

## Motivation

Node's cjs-module-lexer detects a CommonJS module's named exports by **lexically** scanning for `exports.<name> =` / `module.exports` token patterns — no scope analysis ([its README documents renamed identifiers as "DETECTS: NO EXPORTS"](https://github.com/nodejs/cjs-module-lexer)). UMD / CommonJS wrappers bind `exports` / `module` as ordinary function parameters, and the mangler renames them, erasing every named export the lexer can see.

Surfaced by the monitor-oxc mangler runtime test ([failing run](https://github.com/oxc-project/monitor-oxc/actions/runs/28569108563/job/84702922472), previously masked by #24033 / #24036): `archiver` does `import { queue } from "async"`, and `async`'s dist is a UMD bundle — mangled, the factory parameter `exports` became `e` and node fails with `Named export 'queue' not found`. With `reserved: ["exports", "module"]` the output imports correctly (verified end-to-end in node, including running a queue task).

## Why an opt-in list instead of always keeping these names

Renaming them is ecosystem-standard. Verified empirically — minifying the same UMD sample and importing it in node:

| minifier | renames `exports` param? | `import { queue }` from its output | escape hatch |
| --- | --- | --- | --- |
| terser 5.48 | yes | fails | `mangle.reserved` (verified working) |
| uglify-js 3.19 | yes | fails | `reserved` (verified working) |
| esbuild 0.28 `--minify` | yes | fails | none for identifier mangling |
| swc | yes | fails | terser-style `mangle.reserved` ([swc#3424](https://github.com/swc-project/swc/issues/3424)) |

An earlier revision of this PR kept `exports` / `module` unconditionally; the Minification Size check showed the real cost: ~1% minified on UMD-heavy files (d3: +3.3 kB from ~590 `exports` references, flipping it to *larger than esbuild*), because the point is keeping the literal 7-char token at every export site. No other minifier pays this by default — and rolldown bundles bind `exports` / `module` in every CJS interop wrapper, where node's lexer never looks. So oxc gets the same escape hatch as terser/swc, with the same name.

`MangleOptions` loses `Copy` (it now holds the set); construction sites switch to `..MangleOptions::default()` / `clone()`. Exposed in `oxc-minify` NAPI options (`mangle.reserved: string[]`) and the mangler example (`--reserve-exports`).

monitor-oxc will pass `["exports", "module"]` for its mangler runtime test (its corpus imports prebuilt CJS/UMD dists directly).

For reference, esbuild solves this for its *own* node-platform CJS bundles differently — a dead-code annotation `0 && (module.exports = { a, b })` that cjs-module-lexer recognizes ([esbuild#960](https://github.com/evanw/esbuild/issues/960), [cjs-module-lexer#44](https://github.com/nodejs/cjs-module-lexer/issues/44)); verified that pattern also works appended to fully-mangled output. That could be a future zero-size-cost alternative at the minifier level.
2026-07-02 09:31:28 +00:00
Boshen 777f02ae10 refactor(diagnostics)!: return a Diagnostics type instead of Vec<OxcDiagnostic> (#23217)
## Summary

Introduces `oxc_diagnostics::Diagnostics` — a `Vec<OxcDiagnostic>` newtype that knows the difference between errors and warnings — and makes it the standard diagnostics return value across oxc.

- **New type**: `Diagnostics` with `has_errors()`, `has_warnings()`, `errors()`, `warnings()`, plus `Vec`-like ergonomics (Deref, `IntoIterator`, `Extend`, `From`/`Into<Vec>`) so most call sites are unchanged.
- **Return types**: `ParserReturn`, `SemanticBuilderReturn`, `IsolatedDeclarationsReturn`, the React Compiler `TransformResult`/`LintResult`, and `TransformerReturn` now expose `diagnostics: Diagnostics` instead of `errors: Vec<OxcDiagnostic>` (the React Compiler's separate `errors`/`warnings` are merged into one severity-tagged list).
- **Behaviour**: a React Compiler **error** now stops the transform (no output), while **warnings** flow through and the transform continues. `compile()` aborts codegen only when `diagnostics.has_errors()` — warnings alone no longer abort (previously any diagnostic did).

## Breaking change

Consumers reading `*.errors` on these return values must read `*.diagnostics`. React Compiler consumers that read `.errors`/`.warnings` separately should use `diagnostics.has_errors()` / `diagnostics.errors()` / `diagnostics.warnings()`.

Parser/semantic/isolated-declarations fatality is otherwise unchanged. Builds on the React Compiler transform feature (#23201).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-10 14:46:52 +00:00
camc314 275d318e5d docs(napi/minifier): point target to oxc docs (#23102) 2026-06-08 00:43:18 +00:00
Boshen e857b0cfe5 feat(napi/minify): expose legalComments option and result (#20370)
## Summary

- Add `legalComments` and `legalCommentsPath` fields to the NAPI `CodegenOptions` struct, accepting `"none"`, `"inline"`, `"eof"`, `"linked"`, `"external"`
- Add `legalComments: Vec<String>` to `MinifyResult`, populated from `CodegenReturn::legal_comments` when using `"linked"` or `"external"` mode
- Replace the `From<&CodegenOptions>` impl with a `to_codegen_options()` method that validates and maps string values to `oxc_codegen::LegalComment` variants

This enables downstream consumers (e.g. `oxc-webpack-plugin`) to use oxc's built-in legal comment extraction instead of regex-based post-processing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-26 04:31:38 +00:00
Dunqing f134e241bd feat(minifier): support property_write_side_effects option to drop unused property assignments (#20773)
## Summary

Add `property_write_side_effects` option to `TreeShakeOptions` (default `true`). When set to `false`, property assignments like `A.from = () => {}` are considered side-effect-free, enabling the minifier to drop declarations and fresh value bindings with property assignments when unused.

### Examples

```js
// Function declaration + property assignments → eliminated
function A() {}
A.from = () => {}
A.to = () => {}
// Output: (empty)

// Class declaration + property assignment → eliminated
class A {}
A.foo = 1
// Output: (empty)

// Const variable with arrow function → eliminated
const A = () => {}
A.foo = 1
// Output: (empty)

// Const variable with object literal → eliminated
const B = {}
B.foo = 1
// Output: (empty)

// Const variable with function expression → eliminated
const C = function() {}
C.foo = 1
// Output: (empty)
```

### How it works

1. Wires the existing `property_write_side_effects` from `oxc_ecmascript`'s `MayHaveSideEffectsContext` through the minifier's compress options
2. For member expression assignments, checks `may_have_side_effects()` then `is_member_assign_to_unused_binding()`
3. `is_member_assign_to_unused_binding` verifies:
   - Single-level member expression only (`A.foo`, not `a.b.c`)
   - All references to the symbol have the `MemberWriteTarget` flag (from parent PR)
   - Symbol creates a fresh value (`is_fresh_value`) and is not exported
4. Fixed-point loop handles cascading: first pass drops assignments, `exit_program` cleans up references, second pass drops declarations

### Fresh value detection

A symbol is considered "fresh" (cannot alias another binding and property writes are side-effect-free) if it is:
- A **function declaration**
- A **class declaration** without static setters, static accessor properties, or static property definitions with values
- A variable initialized with an **array/arrow function/function expression** literal
- A variable initialized with an **object literal** without setter/getter properties or property values containing setters/getters
- A variable initialized with a **class expression** (same rules as class declarations above)

Non-fresh values (e.g. `const b = a`) are conservatively preserved to prevent breaking aliased references.

#### Setter/getter detection (addresses review feedback)

Following how esbuild, Terser, Rollup, and SWC handle setter cases — all tools preserve property writes when setters are visible in the AST:

- **Static setter methods**: `class A { static set foo(v) { ... } }` → not fresh
- **Static accessor properties**: `class A { static accessor foo = 0 }` → not fresh (auto-generates getter+setter)
- **Static property definitions with values**: `class A { static b = 0 }` → not fresh (matches SWC's conservative approach)
- **Static property values containing setters/getters**: `class A { static b = { set x(v) { ... } } }` → not fresh
- **Object literal setters/getters**: `{ set foo(v) { ... } }` or `{ get foo() { ... } }` → not fresh
- **Nested setters/getters in object property values**: `{ bar: { set x(v) { ... } } }` → not fresh
- **Non-static setters are fine**: `class A { set foo(v) { ... } }` → fresh (instance setters don't affect class-level property writes)

### Limitations

**Only single-level member expressions are supported** (`A.foo`, not `a.b.c`). Deep nested assignments like `a.b.c = 1` are always preserved because intermediate properties could alias external objects:

```js
const exported = {};
const a = { b: exported };
a.b.c = 1; // writes to exported.c — unsafe to drop!
export { exported };
```

Supporting deeper nesting would require full object graph tracking (like Rollup's `ObjectEntity` model), which is a much larger effort for marginal gain.

### Safety guards

Addresses concerns raised in [PR #20730 review](https://github.com/oxc-project/oxc/pull/20730#pullrequestreview-4011376657) and [comment](https://github.com/oxc-project/oxc/pull/20730#issuecomment-4131710117):

- **Aliases**: `const b = a; b.add = 1; export { a }` → kept (not fresh, could mutate exported `a` through alias)
- **Chained access**: `const b = { a }; b.a.add = 1; export { a }` → kept (only single-level member expressions allowed)
- **Exported symbols**: `export function A() {} A.foo = 1;` → kept (observable by importers)
- **Other reads**: `function A() {} A.foo = 1; console.log(A)` → kept (symbol has non-member-write references)
- **Side-effectful RHS**: `function A() {} A.foo = sideEffect()` → kept (`may_have_side_effects` returns true)
- **Globals**: `globalObj.foo = 1` → kept (no local binding)
- **Script mode top-level**: top-level bindings in script mode are kept (they're globals visible to other scripts)
- **Setters**: `class A { static set foo(v) { ... } } A.foo = 1` → kept (setter triggers side effects)
- **Static properties**: `class A { static b = 0 } A.b = 1` → kept (matches SWC's conservative approach)

### Comparison with other tools

| Tool | Approach | Option needed? |
|------|----------|---------------|
| **Rollup** | Structural aliasing analysis — tracks object identity through `LocalVariable.isReassigned` + `ObjectEntity` setter detection. Drops `A.foo = 1` automatically when `A` is a local unreassigned object with no setters. | No (always active) |
| **Rolldown** | Adds explicit [`treeshake.propertyWriteSideEffects`](https://rolldown.rs/reference/InputOptions.treeshake#propertywritesideeffects) option (default `'always'`). Not in upstream Rollup. | Yes |
| **Terser** | Checks `is_constant_expression` on the base object after unwrapping property chain. If the base is constant within scope, drops the assignment. | No (always active, but less precise) |
| **esbuild** | All property assignments always preserved. Rationale: `Object.defineProperty` could install prototype setters. Only escape hatch is `/* @__PURE__ */` IIFE wrapping. | No option available |
| **SWC** | `may_have_side_effects` returns `true` for all assignments unconditionally. Classes with static property definitions are conservatively preserved. | No option available |
| **Oxc (this PR)** | Fresh value tracking (`is_fresh_value`) + `MemberWriteTarget` reference flags + explicit option. Closest to Rollup's aliasing analysis but gated behind an option like Rolldown. | Yes (`property_write_side_effects: false`) |

Closes #14207

## Test plan

- [x] 20 test cases covering all safety scenarios including setter/getter edge cases
- [x] All minifier tests pass (476/476)
- [x] No minsize regressions
- [x] Clippy clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-09 11:13:42 +00:00
Cameron bcd0f6430f fix(napi): disable mimalloc global allocator on android targets (#19214) 2026-02-10 19:06:06 +08:00
sapphi-red 2bf7293c69 feat(mangler)!: enable top_level by default for modules and commonjs (#18278)
I think we can enable `top_level` by default.

refs #18233
2026-02-06 12:48:44 +00:00
Boshen 487601baca fix(napi): disable mimalloc on Windows to fix worker_threads crash (#18923)
## Summary

- Disable mimalloc as the global allocator on Windows for all NAPI packages (`oxc-parser`, `oxc-minify`, `oxc-transform`)
- On Windows, packages now use the system allocator instead

Fixes #15518

## Root Cause

mimalloc has known issues with DLL unloading on Windows when the DLL is loaded by the main thread but unloaded by a worker thread ([mimalloc issue #288](https://github.com/microsoft/mimalloc/issues/288)). The cleanup code has `_mi_is_main_thread()` checks that prevent proper memory cleanup when called from a non-main thread, causing `STATUS_ACCESS_VIOLATION` crashes.

## Platforms with mimalloc enabled

| Platform | mimalloc |
|----------|----------|
| macOS | ✅ |
| Linux x86_64 | ✅ |
| Linux aarch64 | ✅ |
| Windows | ❌ (disabled) |
| FreeBSD | ❌ |
| ARM 32-bit | ❌ |
| WASM | ❌ |

🤖 Generated with [Claude Code](https://claude.ai/code)
2026-02-04 07:44:37 +00:00
sapphi-red af0ca46d70 feat(span)!: use ModuleKind::CommonJS for SourceType::cjs() (#18276)
Follow-up to #18089.

Maybe we should add `SourceType::script()` so that it's easier to create a SourceType with `ModuleType::Script`.
2026-01-20 06:09:26 +00:00
sapphi-red 8e4409a478 feat(minifier): add invalid_import_side_effects option (#17300)
Added a new option (`invalid_import_side_effects`) to disable #16797 as it requires a new assumptions.

refs https://github.com/rolldown/rolldown/pull/7512#issuecomment-3682642929
2025-12-23 09:49:53 +00:00
Boshen ea51b0b5c8 feat(napi)!: standardize function naming with sync suffixes (#15661)
closes #15576

## Summary

Standardizes function naming across all NAPI packages (parser, transform, minify) with a consistent pattern:
- Async functions: `verb` (no suffix)
- Sync functions: `verbSync` (with Sync suffix)

## Changes

### Breaking Changes

**napi/parser:**
- `parseAsync` → `parse` (async)
- `parseSync` remains unchanged (sync)

**napi/transform:**
- `transformAsync` → `transform` (async)
- `transform` → `transformSync` (sync)
- `isolatedDeclaration` → `isolatedDeclarationSync` (sync)
- Added new `isolatedDeclaration` function (async)
- `moduleRunnerTransform` → `moduleRunnerTransformSync` (sync)
- Added new `moduleRunnerTransform` function (async)

**napi/minify:**
- `minify` → `minifySync` (sync)
- Added new `minify` function (async)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-13 12:28:23 +00:00
Boshen f5ce55a40a feat(napi): export all options using wildcard exports
Closes #15618

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 15:38:52 +08:00
sapphi-red 3dc24b5cf5 docs(linter,minifier): always refer as "ES Modules" instead of "ES6 Modules" (#15409)
Nowadays, we normally call them "ES Modules" instead of "ES6 Modules". I made that consistent through the docs.
2025-11-07 10:13:26 +00:00
copilot-swe-agent 1c31cb1a08 feat(napi/minify): expose treeshake options (#15109)
feat(napi/minify): expose `treeshake` options

## Completed:
- [x] Understand the problem: Need to expose TreeShakeOptions to JavaScript users
- [x] Explore existing code structure and TreeShakeOptions in oxc_minifier
- [x] Build the project to ensure clean state
- [x] Add TreeShakeOptions struct with all fields
- [x] Update CompressOptions to accept optional treeshake field
- [x] Implement conversion from NAPI TreeShakeOptions to oxc_minifier::TreeShakeOptions
- [x] Build and verify TypeScript definitions are generated correctly
- [x] Add tests for treeshake options
- [x] Run all tests successfully
- [x] Format code with just fmt
- [x] Address all review feedback:
  - Renamed TreeShakeOptionsNapi to TreeShakeOptions
  - Changed property_read_side_effects to accept boolean | 'always'
  - Changed @default from true to 'always' for property_read_side_effects
  - Reverted From implementation back to use reference (From<&TreeShakeOptions>)
  - Changed to TryFrom to properly handle validation errors
  - Added validation for propertyReadSideEffects string values
  - Fixed clippy lint for inline format args

## Changes Made:
1. Added `TreeShakeOptions` struct with fields:
   - `annotations`: Whether to respect pure annotations (@__PURE__, #__NO_SIDE_EFFECTS__)
   - `manual_pure_functions`: List of function names to treat as pure
   - `property_read_side_effects`: Whether property reads have side effects (boolean | 'always', default: 'always')
   - `unknown_global_side_effects`: Whether accessing global variables has side effects
2. Updated `CompressOptions` to include optional `treeshake` field
3. Implemented `TryFrom<&TreeShakeOptions>` trait with proper error handling for invalid values
4. Added validation that rejects invalid string values for propertyReadSideEffects
5. Added tests covering treeshake functionality including error cases
6. Fixed all lint issues

All tests pass successfully!

<!-- START COPILOT CODING AGENT SUFFIX -->

<details>

<summary>Original prompt</summary>

> Add bindings for treeshake option here:
> https://github.com/oxc-project/oxc/blob/3fbb307367a31817a297dee299fec580912675db/napi/minify/src/options.rs#L90

</details>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
2025-10-30 08:46:30 +00:00
sapphi-red 9e36186e4c feat(napi/minify): expose drop_labels option (#14635)
Exposed the `drop_labels` option from JS API.
2025-10-16 06:07:18 +00:00
sapphi-red d09c7ee8c3 feat(minifier): add drop_labels feature (#14634)
Implemented `drop_labels` feature, which is implemented in [esbuild](https://esbuild.github.io/api/#drop-labels) and [rolldown](https://rolldown.rs/apis/config-options#droplabels).
The plan is to use this in Rolldown.
2025-10-16 06:07:18 +00:00
Boshen 32a41cfab5 fix(napi/minify): s/passes/max_iterations to avoid confusion (#14608)
`max_iterations` does not behave like `passes`, and may change in the future.

It should only be used for debugging purposes, as said on the
comment.
2025-10-15 02:31:18 +00:00
Kentaro Suzuki 14686a43d1 fix(napi/minify): handle boolean values for compress.unused option (#14513)
fixes https://github.com/oxc-project/oxc/issues/14512

Fixes the `compress.unused` option implemented in PR #11796 to properly
accept boolean values from the napi binding.
2025-10-15 09:25:22 +08:00
IWANABETHATGUY c19c9ec4af feat(napi/minify): expose join_vars, sequences, and max_iterations options (#14545)
related to https://github.com/rolldown/rolldown/issues/6477

I use `passes` instead of `max_iterations` since `passes` is commonly used in the Javascript community,
see
- terser: https://github.com/terser/terser#:~:text=passes%20(default%3A%201)%20%2D%2D%20The%20maximum%20number%20of%20times%20to%20run%20compress.%20In%20some%20cases%20more%20than%20one%20pass%20leads%20to%20further%20compressed%20code.%20Keep%20in%20mind%20more%20passes%20will%20take%20more%20time.
- swc:
<img width="626" height="828" alt="image" src="https://github.com/user-attachments/assets/aabc2e10-59a1-43ab-b9e0-52ab35522f85" />
2025-10-14 08:09:57 +00:00
sapphi-red 6fcb0d05c6 fix(minifier)!: receive supported engines instead of ecmascript versions (#13933)
fixes https://github.com/oxc-project/oxc/issues/8905
2025-09-20 14:41:56 +00:00
sapphi-red 593f54ccb6 feat(minifier): add --max-iterations for debugging (#13291)
Added `--max-iterations` to make it easier to debug what has changed in each iteration.
2025-08-25 14:47:06 +00:00
sapphi-red a0e0a91688 feat(oxc_minify_napi): expose CodeGenOptions (#13288)
Follow up to #13239
2025-08-25 07:56:31 +00:00
IWANABETHATGUY d86f732836 chore(oxc_minify_napi): expose more options struct (#13239)
Allow the downstream user to construct a napi options on their own, a
use case in rolldown
https://github.com/rolldown/rolldown/pull/5804/files#diff-56038ed67657ce910f150123cdc2d8ab42bd9bcd9745192c3127f2d837c78865R248,
we need to convert options back to `oxc_minify_napi`, since
`oxc::minify::options` did not implement `toNapi` trait.
2025-08-20 23:18:20 +08:00
Boshen 993db8915c feat(minifier): .minify and .dce methods; run dce in loop (#13026) 2025-08-12 14:58:41 +00:00
Boshen 2093f65e7f feat(napi/minify): make MinifyOptions pub (#12753) 2025-08-02 12:27:12 +00:00
Boshen a286dd4a5b feat(minifier): remove unnecessary 'use strict' directive (#12642) 2025-07-31 05:07:44 +00:00
Boshen 1cf08c06f6 refactor(minifier): make DCE remove more code to align with rollup (#12427) 2025-07-21 13:42:13 +00:00
Boshen fb8289c5d3 feat(minifier): remove unused variable declaration (#11796)
closes #10033
2025-07-16 13:31:44 +08:00
Boshen 395aa5e9ce feat(napi/minify): return parse errors (#12112)
closes #12101
2025-07-07 07:11:02 +00:00
Boshen 7a05e711d5 feat(minifier)!: add Treeshake options (#11786)
closes #9527
2025-06-18 06:42:03 +00:00
Boshen 963167dc76 fix(napi): fix cfg feature on global_allocator 2025-05-15 22:20:42 +08:00
Boshen bd953fce3a fix(napi/minify): need to remove all comments (#10785)
fixes #10784
2025-05-04 06:30:44 +00:00
Boshen b01cb45578 feat(codegen): a way to keep legal comments after minification (#10689)
fixes https://github.com/rolldown/rolldown/issues/4118
fixes #10626

At present, there is no option to keep legal comments when minification is turn on.

This PR allows Codegen to set legal comments with minfication turned on, with the following behavior:

* Inline: only if the attached code is not removed
* Eof: move all legal comments to the end of file, regardless of where they appear
* Linked and External: saved into `CodegenReturn::legal_comments` for the caller to use, regardless of where they appear

Code gets moved around or removed if minifier is turned on, so some legal comments may disappear from the final output.

The recommended legal comment option is Eof.

---

For Rolldown to keep legal comments, it should set `LegalComment::Inline` for chunk codegen and `LegalComment::Eof` for minification codegen.
2025-05-01 06:31:34 +00:00
sapphi-red ea3de06327 feat(mangler): support keep_names option (#9898)
Same with #9873, but built on top of #9897.

close #9873
close #9711
2025-03-23 09:17:42 +00:00
Boshen 6565fc4261 feat(napi): feature gate allocator (#9921)
Rolldown already has allocator enabled, double allocator is a compile error.
2025-03-20 11:40:08 +00:00
sapphi-red dcd356ee32 feat(minifier): support keep_names option (#9867)
Added support for `keep_names: { function: true, class: true }`.
Setting `.name` is makes it difficult to treeshake later on (https://github.com/evanw/esbuild/issues/3965), so it is better to avoid if possible.

This PR only adds support for the compressor. I'll add support for the mangler later on (probably first add required information to the semantics).

refs #9711
2025-03-18 11:24:04 +00:00
Boshen aa3dff887a feat(napi): add mimalloc to parser and transformr (#9859) 2025-03-18 16:51:37 +08:00
LongYinan 84fa5389a3 perf(minify): use mimalloc-safe to replace mimalloc (#9810)
This is maintained by us, and it's upgraded to mimalloc 2.2.2. This pr
also adds mimalloc to `oxc-minify`
2025-03-17 12:38:01 +08:00
Boshen a6c9b0949c docs(napi/minifier): improve documentation (#9736) 2025-03-13 06:28:23 +00:00
Boshen ef6e0cca39 refactor(semantic)!: combine SymbolTable and ScopeTree into Scoping (#9615)
closes #9607
2025-03-09 10:28:12 +00:00
overlookmotel a4a8e7deda style(all): replace #[allow] with #[expect] (#8930)
Replace `#[allow]` with `#[expect]` through the whole codebase.

This also surfaced many places where the `#[allow]` attributes were outdated/pointless and could be removed entirely.

Only places `#[allow]` remains are:

1. In generated code and macros, where lint errors may or may not be triggered depending on the generated code.
2. A few places where clippy malfunctions. These use e.g. `#[allow(unused, clippy::allow_attributes)]`.
2025-02-07 14:50:02 +00:00
Daniel Bulant 6aa2ddeb85 refactor(codegen): accept SymbolTable instead of Mangler (#8829)
It seems to be that the easiest way to rename variables is to use
rename_symbol from SymbolTable and to let it be applied during codegen -
this is what Mangler does.

This PR changes Codegen to accept (any) SymbolTable instead of just
Mangler itself, and for Mangler build output to be a new SymbolTable,
after consuming itself (which makes it clear at the type level if a
Mangler has been built or not).

This also moves the few symbol table helper methods from Mangler to
SymbolTable itself, and adds an example of Mangler use to it's
documentation.

Codegen dependencies now include semantic (which was transient by
mangler before). It's dependency on Mangler could be removed, though
I've left it to allow easy linking of docs (with_symbol_table points
users to Mangler, hopefully helps with migration?)
2025-02-02 17:13:09 +08:00
Boshen 4ad695dcfb feat(napi/minify): implement napi (#8478) 2025-01-14 08:55:55 +00:00
翠 / green db9e93b554 feat(mangler): mangle top level variables (#7907)
Adds `top_level` option which is similar to [terser's `toplevel`
option](https://terser.org/docs/cli-usage/#cli-mangle-options).

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2024-12-15 21:31:41 +08:00
overlookmotel 3858221f45 refactor(global): sort imports (#7883)
Pure refactor. Re-order imports for clarity:

1. `std`
2. External crates
3. `oxc_*` crates
4. Current crate `use crate::...`
5. Super `use super::...`
6. Local modules

This order is from "furthest away" to "closest". This makes it clearer to see what is coming from where.

`cargo +nightly fmt` (#7877) did a lot of the work, but unfortunately `rustfmt` does not have an option to (a) put workspace crates in a separate block from external crates and (b) move `mod` statements to after `use` statements.
2024-12-14 15:07:21 +00:00
Boshen 7fb9d47460 style(rust): cargo +nightly fmt (#7877) 2024-12-14 06:03:31 +00:00