Overall wasm refactoring & improvements

This commit is contained in:
Luke Street 2024-08-21 19:48:58 -06:00
parent 0fccae1049
commit 1f4175dc21
11 changed files with 326 additions and 170 deletions

59
Cargo.lock generated
View File

@ -780,6 +780,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "console_log"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
dependencies = [
"log",
"web-sys",
]
[[package]]
name = "const_format"
version = "0.2.32"
@ -2851,6 +2861,8 @@ dependencies = [
"anyhow",
"arm-attr",
"byteorder",
"console_error_panic_hook",
"console_log",
"cpp_demangle",
"cwdemangle",
"cwextab",
@ -2876,6 +2888,7 @@ dependencies = [
"serde_yaml",
"similar",
"strum",
"tsify-next",
"unarm",
"wasm-bindgen",
]
@ -3860,6 +3873,17 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_derive"
version = "1.0.199"
@ -3871,6 +3895,17 @@ dependencies = [
"syn 2.0.60",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "serde_json"
version = "1.0.116"
@ -4447,6 +4482,30 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tsify-next"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4a645dca4ee0800f5ab60ce166deba2db6a0315de795a2691e138a3d55d756"
dependencies = [
"serde",
"serde-wasm-bindgen",
"tsify-next-macros",
"wasm-bindgen",
]
[[package]]
name = "tsify-next-macros"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d5c06f8a51d759bb58129e30b2631739e7e1e4579fad1f30ac09a6c88e488a6"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.60",
]
[[package]]
name = "ttf-parser"
version = "0.20.0"

View File

@ -23,7 +23,7 @@ mips = ["any-arch", "rabbitizer"]
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
wasm = ["serde_json"]
wasm = ["serde_json", "console_error_panic_hook", "console_log"]
[dependencies]
anyhow = "1.0.82"
@ -40,6 +40,9 @@ serde = { version = "1", features = ["derive"] }
similar = { version = "2.5.0", default-features = false }
strum = { version = "0.26.2", features = ["derive"] }
wasm-bindgen = "0.2.93"
tsify-next = { version = "0.5.4", default-features = false, features = ["js"] }
console_log = { version = "1.0.0", optional = true }
console_error_panic_hook = { version = "0.1.7", optional = true }
# config
globset = { version = "0.4.14", features = ["serde1"], optional = true }

View File

@ -1,53 +1,78 @@
use anyhow::Context;
use prost::Message;
use wasm_bindgen::prelude::*;
use crate::{bindings::diff::DiffResult, diff, obj};
#[wasm_bindgen]
pub fn run_diff(
fn parse_object(
data: Option<Box<[u8]>>,
config: &diff::DiffObjConfig,
) -> Result<Option<obj::ObjInfo>, JsError> {
data.as_ref().map(|data| obj::read::parse(data, config)).transpose().to_js()
}
fn parse_and_run_diff(
left: Option<Box<[u8]>>,
right: Option<Box<[u8]>>,
config: diff::DiffObjConfig,
) -> Result<String, JsError> {
let target = left
.as_ref()
.map(|data| obj::read::parse(data, &config).context("Loading target"))
.transpose()
.map_err(|e| JsError::new(&e.to_string()))?;
let base = right
.as_ref()
.map(|data| obj::read::parse(data, &config).context("Loading base"))
.transpose()
.map_err(|e| JsError::new(&e.to_string()))?;
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)
.map_err(|e| JsError::new(&e.to_string()))?;
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
let out = DiffResult::new(left, right);
serde_json::to_string(&out).map_err(|e| JsError::new(&e.to_string()))
) -> Result<DiffResult, JsError> {
let target = parse_object(left, &config)?;
let base = parse_object(right, &config)?;
run_diff(target.as_ref(), base.as_ref(), config)
}
fn run_diff(
left: Option<&obj::ObjInfo>,
right: Option<&obj::ObjInfo>,
config: diff::DiffObjConfig,
) -> Result<DiffResult, JsError> {
log::debug!("Running diff with config: {:?}", config);
let result = diff::diff_objs(&config, left, right, None).to_js()?;
let left = left.and_then(|o| result.left.as_ref().map(|d| (o, d)));
let right = right.and_then(|o| result.right.as_ref().map(|d| (o, d)));
Ok(DiffResult::new(left, right))
}
// #[wasm_bindgen]
// pub fn run_diff_json(
// left: Option<Box<[u8]>>,
// right: Option<Box<[u8]>>,
// config: diff::DiffObjConfig,
// ) -> Result<String, JsError> {
// let out = run_diff_opt_box(left, right, config)?;
// serde_json::to_string(&out).map_err(|e| JsError::new(&e.to_string()))
// }
#[wasm_bindgen]
pub fn run_diff_proto(
left: Option<Box<[u8]>>,
right: Option<Box<[u8]>>,
config: diff::DiffObjConfig,
) -> Result<Box<[u8]>, JsError> {
let target = left
.as_ref()
.map(|data| obj::read::parse(data, &config).context("Loading target"))
.transpose()
.map_err(|e| JsError::new(&e.to_string()))?;
let base = right
.as_ref()
.map(|data| obj::read::parse(data, &config).context("Loading base"))
.transpose()
.map_err(|e| JsError::new(&e.to_string()))?;
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)
.map_err(|e| JsError::new(&e.to_string()))?;
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
let out = DiffResult::new(left, right);
let out = parse_and_run_diff(left, right, config)?;
Ok(out.encode_to_vec().into_boxed_slice())
}
#[wasm_bindgen(start)]
fn start() -> Result<(), JsError> {
console_error_panic_hook::set_once();
#[cfg(debug_assertions)]
console_log::init_with_level(log::Level::Debug).to_js()?;
#[cfg(not(debug_assertions))]
console_log::init_with_level(log::Level::Info).to_js()?;
Ok(())
}
#[inline]
fn to_js_error(e: impl std::fmt::Display) -> JsError { JsError::new(&e.to_string()) }
trait ToJsResult {
type Output;
fn to_js(self) -> Result<Self::Output, JsError>;
}
impl<T, E: std::fmt::Display> ToJsResult for Result<T, E> {
type Output = T;
fn to_js(self) -> Result<T, JsError> { self.map_err(to_js_error) }
}

View File

@ -1,7 +1,6 @@
use std::collections::HashSet;
use anyhow::Result;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::{
diff::{
@ -18,7 +17,6 @@ pub mod code;
pub mod data;
pub mod display;
#[wasm_bindgen]
#[derive(
Debug,
Copy,
@ -30,6 +28,7 @@ pub mod display;
serde::Serialize,
strum::VariantArray,
strum::EnumMessage,
tsify_next::Tsify,
)]
pub enum X86Formatter {
#[default]
@ -43,7 +42,6 @@ pub enum X86Formatter {
Masm,
}
#[wasm_bindgen]
#[derive(
Debug,
Copy,
@ -55,6 +53,7 @@ pub enum X86Formatter {
serde::Serialize,
strum::VariantArray,
strum::EnumMessage,
tsify_next::Tsify,
)]
pub enum MipsAbi {
#[default]
@ -68,7 +67,6 @@ pub enum MipsAbi {
N64,
}
#[wasm_bindgen]
#[derive(
Debug,
Copy,
@ -80,6 +78,7 @@ pub enum MipsAbi {
serde::Serialize,
strum::VariantArray,
strum::EnumMessage,
tsify_next::Tsify,
)]
pub enum MipsInstrCategory {
#[default]
@ -97,7 +96,6 @@ pub enum MipsInstrCategory {
R5900,
}
#[wasm_bindgen]
#[derive(
Debug,
Copy,
@ -109,6 +107,7 @@ pub enum MipsInstrCategory {
serde::Serialize,
strum::VariantArray,
strum::EnumMessage,
tsify_next::Tsify,
)]
pub enum ArmArchVersion {
#[default]
@ -122,7 +121,6 @@ pub enum ArmArchVersion {
V6K,
}
#[wasm_bindgen]
#[derive(
Debug,
Copy,
@ -134,6 +132,7 @@ pub enum ArmArchVersion {
serde::Serialize,
strum::VariantArray,
strum::EnumMessage,
tsify_next::Tsify,
)]
pub enum ArmR9Usage {
#[default]
@ -154,8 +153,8 @@ pub enum ArmR9Usage {
#[inline]
const fn default_true() -> bool { true }
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, tsify_next::Tsify)]
#[tsify(from_wasm_abi)]
#[serde(default)]
pub struct DiffObjConfig {
pub relax_reloc_diffs: bool,
@ -207,9 +206,6 @@ impl DiffObjConfig {
}
}
#[wasm_bindgen]
pub fn default_diff_obj_config() -> DiffObjConfig { Default::default() }
#[derive(Debug, Clone)]
pub struct ObjSectionDiff {
pub symbols: Vec<ObjSymbolDiff>,

View File

@ -7,5 +7,22 @@ export default [
{languageOptions: {globals: globals.browser}},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{rules: {"semi": [2, "always"]}},
{
rules: {
"semi": [2, "always"],
"@typescript-eslint/no-unused-vars": [
"error",
// https://typescript-eslint.io/rules/no-unused-vars/#benefits-over-typescript
{
"args": "all",
"argsIgnorePattern": "^_",
"caughtErrors": "all",
"caughtErrorsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true
},
],
}
},
];

View File

@ -1,24 +1,24 @@
{
"name": "objdiff-wasm",
"version": "2.0.0-beta.6",
"version": "2.0.0-beta.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "objdiff-wasm",
"version": "2.0.0-beta.6",
"version": "2.0.0-beta.10",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@protobuf-ts/runtime": "2.9.4"
"@protobuf-ts/runtime": "^2.9.4"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@protobuf-ts/plugin": "2.9.4",
"@types/node": "22.4.1",
"esbuild": "0.23.1",
"@protobuf-ts/plugin": "^2.9.4",
"@types/node": "^22.4.1",
"esbuild": "^0.23.1",
"eslint": "^9.9.0",
"globals": "^15.9.0",
"tsup": "8.2.4",
"tsup": "^8.2.4",
"typescript-eslint": "^8.2.0"
}
},

View File

@ -1,6 +1,6 @@
{
"name": "objdiff-wasm",
"version": "2.0.0-beta.6",
"version": "2.0.0-beta.10",
"description": "A local diffing tool for decompilation projects.",
"author": {
"name": "Luke Street",
@ -19,21 +19,21 @@
"types": "dist/main.d.ts",
"scripts": {
"build": "tsup",
"build:all": "npm run build && npm run build:proto && npm run build:wasm",
"build:all": "npm run build:wasm && npm run build:proto && npm run build",
"build:proto": "protoc --ts_out=gen --ts_opt add_pb_suffix,eslint_disable,ts_nocheck,use_proto_field_name --proto_path=../objdiff-core/protos ../objdiff-core/protos/*.proto",
"build:wasm": "cd ../objdiff-core && wasm-pack build --out-dir ../objdiff-wasm/pkg --target web -- --features arm,dwarf,ppc,x86,wasm"
},
"dependencies": {
"@protobuf-ts/runtime": "2.9.4"
"@protobuf-ts/runtime": "^2.9.4"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@protobuf-ts/plugin": "2.9.4",
"@types/node": "22.4.1",
"esbuild": "0.23.1",
"@protobuf-ts/plugin": "^2.9.4",
"@types/node": "^22.4.1",
"esbuild": "^0.23.1",
"eslint": "^9.9.0",
"globals": "^15.9.0",
"tsup": "8.2.4",
"tsup": "^8.2.4",
"typescript-eslint": "^8.2.0"
}
}

View File

@ -2,61 +2,94 @@ import {ArgumentValue, DiffResult, InstructionDiff, RelocationTarget} from "../g
import type {
ArmArchVersion,
ArmR9Usage,
DiffObjConfig as WasmDiffObjConfig,
DiffObjConfig,
MipsAbi,
MipsInstrCategory,
X86Formatter
} from '../pkg';
import {InMessage, OutMessage} from './worker';
import {AnyHandlerData, InMessage, OutMessage} from './worker';
// Export wasm types
export type DiffObjConfig = Omit<Partial<WasmDiffObjConfig>, 'free'>;
export {ArmArchVersion, ArmR9Usage, MipsAbi, MipsInstrCategory, X86Formatter};
export {ArmArchVersion, ArmR9Usage, MipsAbi, MipsInstrCategory, X86Formatter, DiffObjConfig};
// Export protobuf types
export * from '../gen/diff_pb';
interface PromiseCallbacks {
interface PromiseCallbacks<T> {
start: number;
resolve: (value: unknown) => void;
reject: (reason?: unknown) => void;
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: string) => void;
}
let workerInit = false;
let workerCallbacks: PromiseCallbacks | null = null;
let workerCallbacks: PromiseCallbacks<Worker>;
const workerReady = new Promise<Worker>((resolve, reject) => {
workerCallbacks = {start: performance.now(), resolve, reject};
});
export function initialize(workerUrl?: string | URL) {
export async function initialize(data?: {
workerUrl?: string | URL,
wasmUrl?: string | URL, // Relative to worker URL
}): Promise<Worker> {
if (workerInit) {
return;
return workerReady;
}
workerInit = true;
const worker = new Worker(workerUrl || 'worker.js', {type: 'module'});
worker.onmessage = onMessage.bind(null, worker);
worker.onerror = (error) => {
console.error("Worker error", error);
workerCallbacks.reject(error);
let {workerUrl, wasmUrl} = data || {};
if (!workerUrl) {
try {
// Bundlers will convert this into an asset URL
workerUrl = new URL('./worker.js', import.meta.url);
} catch (_) {
workerUrl = 'worker.js';
}
}
if (!wasmUrl) {
try {
// Bundlers will convert this into an asset URL
wasmUrl = new URL('./objdiff_core_bg.wasm', import.meta.url);
} catch (_) {
wasmUrl = 'objdiff_core_bg.js';
}
}
const worker = new Worker(workerUrl, {
name: 'objdiff',
type: 'module',
});
worker.onmessage = onMessage;
worker.onerror = (event) => {
console.error("Worker error", event);
workerCallbacks.reject("Worker failed to initialize, wrong URL?");
};
defer<void>({
type: 'init',
// URL can't be sent directly
wasmUrl: wasmUrl.toString(),
}, worker).then(() => {
workerCallbacks.resolve(worker);
}, (e) => {
workerCallbacks.reject(e);
});
return workerReady;
}
let globalMessageId = 0;
const messageCallbacks = new Map<number, PromiseCallbacks>();
const messageCallbacks = new Map<number, PromiseCallbacks<never>>();
function onMessage(worker: Worker, event: MessageEvent<OutMessage>) {
function onMessage(event: MessageEvent<OutMessage>) {
switch (event.data.type) {
case 'ready':
workerCallbacks.resolve(worker);
break;
case 'result': {
const {messageId, result} = event.data;
const {result, error, messageId} = event.data;
const callbacks = messageCallbacks.get(messageId);
if (callbacks) {
const end = performance.now();
console.debug(`Message ${messageId} took ${end - callbacks.start}ms`);
messageCallbacks.delete(messageId);
callbacks.resolve(result);
if (error != null) {
callbacks.reject(error);
} else {
callbacks.resolve(result as never);
}
} else {
console.warn(`Unknown message ID ${messageId}`);
}
@ -65,11 +98,8 @@ function onMessage(worker: Worker, event: MessageEvent<OutMessage>) {
}
}
async function defer<T>(message: Omit<InMessage, 'messageId'>): Promise<T> {
if (!workerInit) {
throw new Error('Worker not initialized');
}
const worker = await workerReady;
async function defer<T>(message: AnyHandlerData, worker?: Worker): Promise<T> {
worker = worker || await initialize();
const messageId = globalMessageId++;
const promise = new Promise<T>((resolve, reject) => {
messageCallbacks.set(messageId, {start: performance.now(), resolve, reject});
@ -77,17 +107,17 @@ async function defer<T>(message: Omit<InMessage, 'messageId'>): Promise<T> {
worker.postMessage({
...message,
messageId
});
} as InMessage);
return promise;
}
export async function runDiff(left: Uint8Array | undefined, right: Uint8Array | undefined, config?: DiffObjConfig): Promise<DiffResult> {
const data = await defer<Uint8Array>({
type: 'run_diff',
type: 'run_diff_proto',
left,
right,
config
} as InMessage);
});
const parseStart = performance.now();
const result = DiffResult.fromBinary(data, {readUnknownField: false});
const end = performance.now();
@ -148,11 +178,6 @@ export type DiffTextSpacing = DiffTextBase & {
count: number,
};
// TypeScript workaround for oneof types
export function oneof<T extends { oneofKind: string }>(type: T): T & { oneofKind: string } {
return type as T & { oneofKind: string };
}
// Native JavaScript implementation of objdiff_core::diff::display::display_diff
export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text: DiffText) => void) {
const ins = diff.instruction;
@ -173,7 +198,7 @@ export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text:
if (i === 0) {
cb({type: 'spacing', count: 1});
}
const arg = oneof(ins.arguments[i].value);
const arg = ins.arguments[i].value;
const diff_index = diff.arg_diff[i]?.diff_index;
switch (arg.oneofKind) {
case "plain_text":
@ -184,7 +209,7 @@ export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text:
break;
case "relocation": {
const reloc = ins.relocation!;
cb({type: 'symbol', target: reloc.target, diff_index});
cb({type: 'symbol', target: reloc.target!, diff_index});
break;
}
case "branch_dest":

View File

@ -1,81 +1,93 @@
import wasmInit, {default_diff_obj_config, run_diff_proto} from '../pkg';
import {DiffObjConfig} from "./main";
import wasmInit, * as exports from '../pkg';
self.postMessage({type: 'init'} as OutMessage);
await wasmInit({});
self.postMessage({type: 'ready'} as OutMessage);
type ExtractParam<T> = {
[K in keyof T]: T[K] extends (arg1: infer U, ...args: any[]) => any ? U & { type: K } : never;
}[keyof T];
type HandlerData = ExtractParam<{
run_diff: typeof run_diff,
}>;
const handlers: {
[K in HandlerData['type']]: (data: Omit<HandlerData, 'type'>) => unknown
} = {
'run_diff': run_diff,
const handlers = {
init: init,
// run_diff_json: run_diff_json,
run_diff_proto: run_diff_proto,
} as const;
type ExtractData<T> = T extends (arg: infer U) => Promise<unknown> ? U : never;
type HandlerData = {
[K in keyof typeof handlers]: { type: K } & ExtractData<typeof handlers[K]>;
};
function run_diff({left, right, config}: {
left: Uint8Array | undefined,
right: Uint8Array | undefined,
config?: DiffObjConfig
}): Uint8Array {
const cfg = default_diff_obj_config();
if (config) {
for (const key in config) {
if (key in config) {
cfg[key] = config[key];
}
}
let wasmReady: Promise<void> | null = null;
async function init({wasmUrl}: { wasmUrl?: string }): Promise<void> {
if (wasmReady != null) {
throw new Error('Already initialized');
}
return run_diff_proto(left, right, cfg);
wasmReady = wasmInit({module_or_path: wasmUrl})
.then(() => {
});
return wasmReady;
}
export type InMessage = HandlerData & { messageId: number };
async function initIfNeeded() {
if (wasmReady == null) {
await init({});
}
return wasmReady;
}
export type OutMessage = ({
// async function run_diff_json({left, right, config}: {
// left: Uint8Array | undefined,
// right: Uint8Array | undefined,
// config?: exports.DiffObjConfig,
// }): Promise<string> {
// config = config || exports.default_diff_obj_config();
// return exports.run_diff_json(left, right, cfg);
// }
async function run_diff_proto({left, right, config}: {
left: Uint8Array | undefined,
right: Uint8Array | undefined,
config?: exports.DiffObjConfig,
}): Promise<Uint8Array> {
config = config || {};
return exports.run_diff_proto(left, right, config);
}
export type AnyHandlerData = HandlerData[keyof HandlerData];
export type InMessage = AnyHandlerData & { messageId: number };
export type OutMessage = {
type: 'result',
result: unknown | null,
error: unknown | null,
} | {
type: 'init',
msg: string
} | {
type: 'ready',
msg: string
}) & { messageId: number };
error: string | null,
messageId: number,
};
self.onmessage = async (event: MessageEvent<InMessage>) => {
self.onmessage = (event: MessageEvent<InMessage>) => {
const data = event.data;
const handler = handlers[data.type];
if (handler) {
try {
const messageId = data?.messageId;
(async () => {
if (!data) {
throw new Error('No data');
}
const handler = handlers[data.type];
if (handler) {
if (data.type !== 'init') {
await initIfNeeded();
}
const start = performance.now();
const result = handler(data);
const result = await handler(data as never);
const end = performance.now();
console.debug(`Worker message ${data.messageId} took ${end - start}ms`);
self.postMessage({
type: 'result',
result: result,
error: null,
messageId: data.messageId
});
} catch (error) {
self.postMessage({
type: 'result',
result: null,
error: error,
messageId: data.messageId
});
messageId,
} as OutMessage);
} else {
throw new Error(`No handler for ${data.type}`);
}
} else {
})().catch(error => {
self.postMessage({
type: 'result',
result: null,
error: `No handler for ${data.type}`,
messageId: data.messageId
});
}
error: error.toString(),
messageId,
} as OutMessage);
});
};

View File

@ -1,8 +1,9 @@
{
"compilerOptions": {
"esModuleInterop": true,
"module": "ES2022",
"moduleResolution": "Node",
"strict": true,
"target": "ES2022",
"esModuleInterop": true
}
}

View File

@ -1,15 +1,33 @@
import {defineConfig} from 'tsup';
import fs from 'node:fs/promises';
export default defineConfig({
entry: ['src/main.ts', 'src/worker.ts'],
clean: true,
dts: true,
format: 'esm',
sourcemap: true,
splitting: false,
target: ['es2022', 'chrome89', 'edge89', 'firefox89', 'safari15', 'node14.8'],
async onSuccess() {
await fs.copyFile('pkg/objdiff_core_bg.wasm', 'dist/objdiff_core_bg.wasm');
export default defineConfig([
// Build main library
{
entry: ['src/main.ts'],
clean: true,
dts: true,
format: 'esm',
outDir: 'dist',
skipNodeModulesBundle: true,
sourcemap: true,
splitting: false,
target: 'es2022',
},
// Build web worker
{
entry: ['src/worker.ts'],
clean: true,
dts: true,
format: 'esm', // type: 'module'
minify: true,
outDir: 'dist',
sourcemap: true,
splitting: false,
target: 'es2022',
// https://github.com/egoist/tsup/issues/278
async onSuccess() {
await fs.copyFile('pkg/objdiff_core_bg.wasm', 'dist/objdiff_core_bg.wasm');
}
}
});
]);