Skip to content

Commit

Permalink
feat(napi/minify): implement napi
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Jan 14, 2025
1 parent 7a8200c commit 03613c1
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 31 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/oxc_minifier/examples/minifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn minify(
let mut program = ret.program;
let options = MinifierOptions {
mangle: mangle.then(MangleOptions::default),
compress: CompressOptions::default(),
compress: Some(CompressOptions::default()),
};
let ret = Minifier::new(options).build(allocator, &mut program);
CodeGenerator::new()
Expand Down
21 changes: 13 additions & 8 deletions crates/oxc_minifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod tester;
use oxc_allocator::Allocator;
use oxc_ast::ast::Program;
use oxc_mangler::Mangler;
use oxc_semantic::SemanticBuilder;
use oxc_semantic::{SemanticBuilder, Stats};

pub use oxc_mangler::MangleOptions;

Expand All @@ -21,12 +21,12 @@ pub use crate::{ast_passes::CompressorPass, compressor::Compressor, options::Com
#[derive(Debug, Clone, Copy)]
pub struct MinifierOptions {
pub mangle: Option<MangleOptions>,
pub compress: CompressOptions,
pub compress: Option<CompressOptions>,
}

impl Default for MinifierOptions {
fn default() -> Self {
Self { mangle: Some(MangleOptions::default()), compress: CompressOptions::default() }
Self { mangle: Some(MangleOptions::default()), compress: Some(CompressOptions::default()) }
}
}

Expand All @@ -44,11 +44,16 @@ impl Minifier {
}

pub fn build<'a>(self, allocator: &'a Allocator, program: &mut Program<'a>) -> MinifierReturn {
let semantic = SemanticBuilder::new().build(program).semantic;
let stats = semantic.stats();
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
Compressor::new(allocator, self.options.compress)
.build_with_symbols_and_scopes(symbols, scopes, program);
let stats = if let Some(compress) = self.options.compress {
let semantic = SemanticBuilder::new().build(program).semantic;
let stats = semantic.stats();
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
Compressor::new(allocator, compress)
.build_with_symbols_and_scopes(symbols, scopes, program);
stats
} else {
Stats::default()
};
let mangler = self.options.mangle.map(|options| {
let semantic = SemanticBuilder::new().with_stats(stats).build(program).semantic;
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,15 +271,15 @@ impl Oxc {
let compress_options = minifier_options.compress_options.unwrap_or_default();
let options = MinifierOptions {
mangle: minifier_options.mangle.unwrap_or_default().then(MangleOptions::default),
compress: if minifier_options.compress.unwrap_or_default() {
compress: Some(if minifier_options.compress.unwrap_or_default() {
CompressOptions {
drop_console: compress_options.drop_console,
drop_debugger: compress_options.drop_debugger,
..CompressOptions::all_false()
}
} else {
CompressOptions::all_false()
},
}),
};
Minifier::new(options).build(&allocator, &mut program).mangler
} else {
Expand Down
2 changes: 2 additions & 0 deletions napi/minify/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ oxc_allocator = { workspace = true }
oxc_codegen = { workspace = true }
oxc_minifier = { workspace = true }
oxc_parser = { workspace = true }
oxc_sourcemap = { workspace = true, features = ["napi", "rayon"] }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true }

napi = { workspace = true }
napi-derive = { workspace = true }
Expand Down
88 changes: 87 additions & 1 deletion napi/minify/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,90 @@
/* auto-generated by NAPI-RS */
/* eslint-disable */
export declare function minify(filename: string, sourceText: string): string
export interface CodegenOptions {
/**
* Remove whitespace.
*
* @default true
*/
whitespace?: boolean
}

export interface CompressOptions {
/**
* Enables optional catch or nullish-coalescing operator if targeted higher.
*
* @default 'es2015'
*/
target?: string
/**
* Pass true to discard calls to `console.*`.
*
* @default false
*/
dropConsole?: boolean
/**
* Remove `debugger;` statements.
*
* @default true
*/
dropDebugger?: boolean
}

export interface ErrorLabel {
message?: string
start: number
end: number
}

export interface MangleOptions {
/** Pass true to mangle names declared in the top level scope. */
toplevel?: boolean
/** Debug mangled names. */
debug?: boolean
}

/**
* Minify synchronously.
*
* # Errors
*
* * Fails to parse the options.
*/
export declare function minify(filename: string, sourceText: string, options?: MinifyOptions | undefined | null): MinifyResult

export interface MinifyOptions {
compress?: boolean | CompressOptions
mangle?: boolean | MangleOptions
codegen?: boolean | CodegenOptions
sourcemap?: boolean
}

export interface MinifyResult {
code: string
map?: SourceMap
}

export interface OxcError {
severity: Severity
message: string
labels: Array<ErrorLabel>
helpMessage?: string
}

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>
}

1 change: 1 addition & 0 deletions napi/minify/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,4 @@ if (!nativeBinding) {
}

module.exports.minify = nativeBinding.minify
module.exports.Severity = nativeBinding.Severity
5 changes: 1 addition & 4 deletions napi/minify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
"scripts": {
"build-dev": "napi build --platform",
"build": "napi build --platform --release",
"test": "echo 'skip'"
},
"engines": {
"node": ">=14.*"
"test": "vitest --typecheck run ./test"
},
"napi": {
"binaryName": "minify",
Expand Down
60 changes: 45 additions & 15 deletions napi/minify/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,59 @@
#![allow(clippy::needless_pass_by_value)]

mod options;

use std::path::PathBuf;

use napi::Either;
use napi_derive::napi;

use oxc_allocator::Allocator;
use oxc_codegen::{Codegen, CodegenOptions};
use oxc_minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions};
use oxc_minifier::Minifier;
use oxc_parser::Parser;
use oxc_span::SourceType;

#[allow(clippy::needless_pass_by_value)]
use crate::options::{MinifyOptions, MinifyResult};

/// Minify synchronously.
///
/// # Errors
///
/// * Fails to parse the options.
#[napi]
pub fn minify(filename: String, source_text: String) -> String {
pub fn minify(
filename: String,
source_text: String,
options: Option<MinifyOptions>,
) -> napi::Result<MinifyResult> {
let options = options.unwrap_or_default();

let minifier_options = match oxc_minifier::MinifierOptions::try_from(&options) {
Ok(options) => options,
Err(error) => return Err(napi::Error::from_reason(&error)),
};

let allocator = Allocator::default();

let source_type = SourceType::from_path(&filename).unwrap_or_default().with_typescript(true);

let mut program = Parser::new(&allocator, &source_text, source_type).parse().program;

let mangler = Minifier::new(MinifierOptions {
mangle: Some(MangleOptions::default()),
compress: CompressOptions::default(),
})
.build(&allocator, &mut program)
.mangler;

Codegen::new()
.with_options(CodegenOptions { minify: true, ..CodegenOptions::default() })
.with_mangler(mangler)
.build(&program)
.code
let mangler = Minifier::new(minifier_options).build(&allocator, &mut program).mangler;

let mut codegen_options = match &options.codegen {
Some(Either::A(false)) => CodegenOptions { minify: false, ..CodegenOptions::default() },
None | Some(Either::A(true)) => {
CodegenOptions { minify: true, ..CodegenOptions::default() }
}
Some(Either::B(o)) => CodegenOptions::from(o),
};

if options.sourcemap == Some(true) {
codegen_options.source_map_path = Some(PathBuf::from(filename));
}

let ret = Codegen::new().with_options(codegen_options).with_mangler(mangler).build(&program);

Ok(MinifyResult { code: ret.code, map: ret.map.map(oxc_sourcemap::napi::SourceMap::from) })
}
Loading

0 comments on commit 03613c1

Please sign in to comment.