Files
oxc-project__oxc/napi/minify/index.d.ts
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

287 lines
7.3 KiB
TypeScript

/* auto-generated by NAPI-RS */
/* eslint-disable */
export interface CodegenOptions {
/**
* Remove whitespace.
*
* @default true
*/
removeWhitespace?: boolean
/**
* How to handle legal comments (comments containing `@license`, `@preserve`, or starting with `//!`/`/*!`).
*
* * `"none"` - Do not preserve any legal comments.
* * `"inline"` - Preserve all legal comments inline.
* * `"eof"` - Move all legal comments to the end of the file.
* * `"external"` - Extract legal comments without linking.
* * `{ linked: "path/to/legal.txt" }` - Extract legal comments and add a link comment to the given path.
*
* @default "none" (when minifying)
*/
legalComments?: 'none' | 'inline' | 'eof' | 'external' | { linked: string }
}
export interface CompressOptions {
/**
* Set desired EcmaScript standard version for output.
*
* Set `esnext` to enable all target highering.
*
* Example:
*
* * `'es2015'`
* * `['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11']`
*
* @default 'esnext'
*
* @see [oxc#target](https://oxc.rs/docs/guide/usage/transformer/lowering#target)
*/
target?: string | Array<string>
/**
* Pass true to discard calls to `console.*`.
*
* @default false
*/
dropConsole?: boolean
/**
* Remove `debugger;` statements.
*
* @default true
*/
dropDebugger?: boolean
/**
* Pass `true` to drop unreferenced functions and variables.
*
* Simple direct variable assignments do not count as references unless set to `keep_assign`.
* @default true
*/
unused?: boolean | 'keep_assign'
/** Keep function / class names. */
keepNames?: CompressOptionsKeepNames
/**
* Join consecutive var, let and const statements.
*
* @default true
*/
joinVars?: boolean
/**
* Join consecutive simple statements using the comma operator.
*
* `a; b` -> `a, b`
*
* @default true
*/
sequences?: boolean
/**
* Set of label names to drop from the code.
*
* Labeled statements matching these names will be removed during minification.
*
* @default []
*/
dropLabels?: Array<string>
/** Limit the maximum number of iterations for debugging purpose. */
maxIterations?: number
/** Treeshake options. */
treeshake?: TreeShakeOptions
}
export interface CompressOptionsKeepNames {
/**
* Keep function names so that `Function.prototype.name` is preserved.
*
* This does not guarantee that the `undefined` name is preserved.
*
* @default false
*/
function: boolean
/**
* Keep class names so that `Class.prototype.name` is preserved.
*
* This does not guarantee that the `undefined` name is preserved.
*
* @default false
*/
class: boolean
}
export interface LegalCommentsLinked {
/**
* Extract legal comments and write them to the given path, with a link
* comment appended to the generated code.
*/
linked: string
}
export declare const enum LegalCommentsMode {
/** Do not preserve any legal comments. */
None = 'none',
/** Preserve all legal comments inline. */
Inline = 'inline',
/** Move all legal comments to the end of the file. */
Eof = 'eof',
/** Extract legal comments without linking. */
External = 'external'
}
export interface MangleOptions {
/**
* Pass `true` to mangle names declared in the top level scope.
*
* @default true for modules and commonjs, otherwise false
*/
toplevel?: boolean
/**
* Preserve `name` property for functions and classes.
*
* @default false
*/
keepNames?: boolean | MangleOptionsKeepNames
/**
* Names that bindings must not be renamed to, and that bindings already
* carrying them keep. Equivalent to terser's `mangle.reserved`.
*
* Pass `['exports', 'module']` when minifying prebuilt CommonJS / UMD files
* that Node consumers `import` directly, so Node's cjs-module-lexer can still
* detect the mangled module's named exports.
*
* @default []
*/
reserved?: Array<string>
/** Debug mangled names. */
debug?: boolean
}
export interface MangleOptionsKeepNames {
/**
* Preserve `name` property for functions.
*
* @default false
*/
function: boolean
/**
* Preserve `name` property for classes.
*
* @default false
*/
class: boolean
}
/**
* Minify asynchronously.
*
* Note: This function can be slower than `minifySync` due to the overhead of spawning a thread.
*/
export declare function minify(filename: string, sourceText: string, options?: MinifyOptions | undefined | null): Promise<MinifyResult>
export interface MinifyOptions {
/** Use when minifying an ES module. */
module?: boolean
compress?: boolean | CompressOptions
mangle?: boolean | MangleOptions
codegen?: boolean | CodegenOptions
sourcemap?: boolean
}
export interface MinifyResult {
code: string
map?: SourceMap
errors: Array<OxcError>
/**
* Legal comments extracted from the source code.
* Only populated when `codegen.legalComments` is `"linked"` or `"external"`.
*/
legalComments: Array<string>
}
/** Minify synchronously. */
export declare function minifySync(filename: string, sourceText: string, options?: MinifyOptions | undefined | null): MinifyResult
export interface TreeShakeOptions {
/**
* Whether to respect the pure annotations.
*
* Pure annotations are comments that mark an expression as pure.
* For example: @__PURE__ or #__NO_SIDE_EFFECTS__.
*
* @default true
*/
annotations?: boolean
/**
* Whether to treat this function call as pure.
*
* This function is called for normal function calls, new calls, and
* tagged template calls.
*/
manualPureFunctions?: Array<string>
/**
* Whether property read accesses have side effects.
*
* @default 'always'
*/
propertyReadSideEffects?: boolean | 'always'
/**
* Whether property write accesses (assignments to member expressions) have side effects.
*
* When false, assignments like `obj.prop = value` are considered side-effect-free
* (assuming the object and value expressions themselves are side-effect-free).
*
* @default true
*/
propertyWriteSideEffects?: boolean
/**
* Whether accessing a global variable has side effects.
*
* Accessing a non-existing global variable will throw an error.
* Global variable may be a getter that has side effects.
*
* @default true
*/
unknownGlobalSideEffects?: boolean
/**
* Whether invalid import statements have side effects.
*
* Accessing a non-existing import name will throw an error.
* Also import statements that cannot be resolved will throw an error.
*
* @default true
*/
invalidImportSideEffects?: boolean
}
export interface Comment {
type: 'Line' | 'Block'
value: string
start: number
end: number
}
export interface ErrorLabel {
message: string | null
start: number
end: number
}
export interface OxcError {
severity: Severity
message: string
labels: Array<ErrorLabel>
helpMessage: string | null
codeframe: string | null
}
export declare const enum Severity {
Error = 'Error',
Warning = 'Warning',
Advice = 'Advice'
}
export interface SourceMap {
file?: string
mappings: string
names: Array<string>
sourceRoot?: string
sources: Array<string>
sourcesContent?: Array<string>
version: number
x_google_ignoreList?: Array<number>
}