mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(napi): add napi minifier (#6053)
This commit is contained in:
parent
2da9a4d298
commit
c519182359
8 changed files with 456 additions and 0 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -1715,6 +1715,20 @@ dependencies = [
|
|||
"pico-args",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxc_minify_napi"
|
||||
version = "0.30.3"
|
||||
dependencies = [
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
"oxc_allocator",
|
||||
"oxc_codegen",
|
||||
"oxc_minifier",
|
||||
"oxc_parser",
|
||||
"oxc_span",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxc_minsize"
|
||||
version = "0.0.0"
|
||||
|
|
|
|||
37
napi/minify/Cargo.toml
Normal file
37
napi/minify/Cargo.toml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[package]
|
||||
name = "oxc_minify_napi"
|
||||
version = "0.30.3"
|
||||
authors.workspace = true
|
||||
categories.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
publish = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
description.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
oxc_allocator = { workspace = true }
|
||||
oxc_codegen = { workspace = true }
|
||||
oxc_minifier = { workspace = true }
|
||||
oxc_parser = { workspace = true }
|
||||
oxc_span = { workspace = true }
|
||||
|
||||
napi = { workspace = true }
|
||||
napi-derive = { workspace = true }
|
||||
|
||||
[package.metadata.cargo-shear]
|
||||
ignored = ["napi"]
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = { workspace = true }
|
||||
3
napi/minify/build.rs
Normal file
3
napi/minify/build.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
napi_build::setup();
|
||||
}
|
||||
3
napi/minify/index.d.ts
vendored
Normal file
3
napi/minify/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/* auto-generated by NAPI-RS */
|
||||
/* eslint-disable */
|
||||
export declare function minify(filename: string, sourceText: string): string;
|
||||
345
napi/minify/index.js
Normal file
345
napi/minify/index.js
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
// prettier-ignore
|
||||
/* eslint-disable */
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
const { readFileSync } = require('fs');
|
||||
|
||||
let nativeBinding = null;
|
||||
const loadErrors = [];
|
||||
|
||||
const isMusl = () => {
|
||||
let musl = false;
|
||||
if (process.platform === 'linux') {
|
||||
musl = isMuslFromFilesystem();
|
||||
if (musl === null) {
|
||||
musl = isMuslFromReport();
|
||||
}
|
||||
if (musl === null) {
|
||||
musl = isMuslFromChildProcess();
|
||||
}
|
||||
}
|
||||
return musl;
|
||||
};
|
||||
|
||||
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-');
|
||||
|
||||
const isMuslFromFilesystem = () => {
|
||||
try {
|
||||
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const isMuslFromReport = () => {
|
||||
const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null;
|
||||
if (!report) {
|
||||
return null;
|
||||
}
|
||||
if (report.header && report.header.glibcVersionRuntime) {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(report.sharedObjects)) {
|
||||
if (report.sharedObjects.some(isFileMusl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isMuslFromChildProcess = () => {
|
||||
try {
|
||||
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl');
|
||||
} catch (e) {
|
||||
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function requireNative() {
|
||||
if (process.platform === 'android') {
|
||||
if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./minify.android-arm64.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-android-arm64');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else if (process.arch === 'arm') {
|
||||
try {
|
||||
return require('./minify.android-arm-eabi.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-android-arm-eabi');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`));
|
||||
}
|
||||
} else if (process.platform === 'win32') {
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./minify.win32-x64-msvc.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-win32-x64-msvc');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else if (process.arch === 'ia32') {
|
||||
try {
|
||||
return require('./minify.win32-ia32-msvc.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-win32-ia32-msvc');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./minify.win32-arm64-msvc.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-win32-arm64-msvc');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`));
|
||||
}
|
||||
} else if (process.platform === 'darwin') {
|
||||
try {
|
||||
return require('./minify.darwin-universal.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-darwin-universal');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./minify.darwin-x64.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-darwin-x64');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./minify.darwin-arm64.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-darwin-arm64');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`));
|
||||
}
|
||||
} else if (process.platform === 'freebsd') {
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./minify.freebsd-x64.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-freebsd-x64');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./minify.freebsd-arm64.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-freebsd-arm64');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`));
|
||||
}
|
||||
} else if (process.platform === 'linux') {
|
||||
if (process.arch === 'x64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./minify.linux-x64-musl.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-x64-musl');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./minify.linux-x64-gnu.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-x64-gnu');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./minify.linux-arm64-musl.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-arm64-musl');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./minify.linux-arm64-gnu.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-arm64-gnu');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'arm') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./minify.linux-arm-musleabihf.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-arm-musleabihf');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./minify.linux-arm-gnueabihf.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-arm-gnueabihf');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'riscv64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./minify.linux-riscv64-musl.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-riscv64-musl');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./minify.linux-riscv64-gnu.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-riscv64-gnu');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'ppc64') {
|
||||
try {
|
||||
return require('./minify.linux-ppc64-gnu.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-ppc64-gnu');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else if (process.arch === 's390x') {
|
||||
try {
|
||||
return require('./minify.linux-s390x-gnu.node');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
try {
|
||||
return require('@oxc-minify/binding-linux-s390x-gnu');
|
||||
} catch (e) {
|
||||
loadErrors.push(e);
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`));
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`));
|
||||
}
|
||||
}
|
||||
|
||||
nativeBinding = requireNative();
|
||||
|
||||
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
||||
try {
|
||||
nativeBinding = require('./minify.wasi.cjs');
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
if (!nativeBinding) {
|
||||
try {
|
||||
nativeBinding = require('@oxc-minify/binding-wasm32-wasi');
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nativeBinding) {
|
||||
if (loadErrors.length > 0) {
|
||||
// TODO Link to documentation with potential fixes
|
||||
// - The package owner could build/publish bindings for this arch
|
||||
// - The user may need to bundle the correct files
|
||||
// - The user may need to re-install node_modules to get new packages
|
||||
throw new Error('Failed to load native binding', { cause: loadErrors });
|
||||
}
|
||||
throw new Error(`Failed to load native binding`);
|
||||
}
|
||||
|
||||
module.exports.minify = nativeBinding.minify;
|
||||
24
napi/minify/package.json
Normal file
24
napi/minify/package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "@oxc-minify/binding",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "napi build --platform --release",
|
||||
"test": "node test.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.*"
|
||||
},
|
||||
"napi": {
|
||||
"binaryName": "minify",
|
||||
"targets": [
|
||||
"x86_64-pc-windows-msvc",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-musl",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-darwin"
|
||||
]
|
||||
}
|
||||
}
|
||||
28
napi/minify/src/lib.rs
Normal file
28
napi/minify/src/lib.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use napi_derive::napi;
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_codegen::{Codegen, CodegenOptions};
|
||||
use oxc_minifier::{CompressOptions, Minifier, MinifierOptions};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_span::SourceType;
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[napi]
|
||||
pub fn minify(filename: String, source_text: String) -> String {
|
||||
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: true, compress: CompressOptions::default() })
|
||||
.build(&allocator, &mut program)
|
||||
.mangler;
|
||||
|
||||
Codegen::new()
|
||||
.with_options(CodegenOptions { minify: true, ..CodegenOptions::default() })
|
||||
.with_mangler(mangler)
|
||||
.with_capacity(source_text.len())
|
||||
.build(&program)
|
||||
.source_text
|
||||
}
|
||||
|
|
@ -49,6 +49,8 @@ importers:
|
|||
specifier: ^5.4.5
|
||||
version: 5.6.2
|
||||
|
||||
napi/minify: {}
|
||||
|
||||
napi/parser:
|
||||
devDependencies:
|
||||
'@napi-rs/cli':
|
||||
|
|
|
|||
Loading…
Reference in a new issue