use std::{error::Error, str::FromStr}; use anyhow::{Context, Result}; use bytes::Buf; use js::{ RewriteResult, Rewriter, cfg::{Config, Flags, UrlRewriter}, }; use oxc::allocator::{Allocator, StringBuilder}; use url::Url; use urlencoding::encode; use crate::RewriterOptions; struct NativeUrlRewriter; impl UrlRewriter for NativeUrlRewriter { fn rewrite( &self, _cfg: &Config, flags: &Flags, url: &str, builder: &mut StringBuilder, _module: bool, ) -> Result<(), Box> { let base = Url::from_str(&flags.base)?; builder.push_str(encode(base.join(url)?.as_str()).as_ref()); Ok(()) } } #[derive(Debug)] enum RewriteType<'a> { Insert { pos: u32, size: u32 }, Replace { start: u32, end: u32, str: &'a [u8] }, } pub struct NativeRewriter { alloc: Allocator, rewriter: Rewriter, } impl NativeRewriter { #[allow(dead_code)] pub fn default() -> Self { Self::new(&RewriterOptions::default()) } pub fn new(cfg: &RewriterOptions) -> Self { let rewriter = Rewriter::new( Config { prefix: cfg.prefix.clone(), wrapfn: cfg.wrapfn.clone(), wrapthisfn: cfg.wrapthisfn.clone(), importfn: cfg.importfn.clone(), rewritefn: cfg.rewritefn.clone(), metafn: cfg.metafn.clone(), setrealmfn: cfg.setrealmfn.clone(), pushsourcemapfn: cfg.pushsourcemapfn.clone(), }, NativeUrlRewriter, ); Self { alloc: Allocator::new(), rewriter, } } #[allow(dead_code)] pub fn rewrite_default(&self, data: &str) -> Result> { self.rewrite(data, &RewriterOptions::default()) } pub fn rewrite(&self, data: &str, cfg: &RewriterOptions) -> Result> { self.rewriter .rewrite( &self.alloc, data, Flags { base: cfg.base.clone(), sourcetag: cfg.sourcetag.clone(), is_module: cfg.is_module, capture_errors: cfg.capture_errors, do_sourcemaps: cfg.do_sourcemaps, scramitize: cfg.scramitize, strict_rewrites: cfg.strict_rewrites, }, ) .context("failed to rewrite file") } pub fn reset(&mut self) { self.alloc.reset(); } pub fn unrewrite(res: &RewriteResult) -> Vec { let js = res.js.as_slice(); let mut map = res.sourcemap.as_slice(); let rewrite_cnt = map.get_u32_le(); let mut rewrites = Vec::with_capacity(rewrite_cnt as usize); for x in 0..rewrite_cnt { let pos = map.get_u32_le(); let size = map.get_u32_le(); let ty = map.get_u8(); if ty == 0 { rewrites.push(RewriteType::Insert { pos, size }); } else if ty == 1 { let len = map.get_u32_le(); let (str, new) = map.split_at(len as usize); map = new; rewrites.push(RewriteType::Replace { start: pos, end: pos + size, str, }); } else { panic!( "{x} {ty} {:X?} {:#?}", &map[0..10], &rewrites.last_chunk::<3>() ) } } let mut out = Vec::with_capacity(res.js.len()); let mut cursor: u32 = 0; for rewrite in rewrites { match rewrite { RewriteType::Insert { pos, size } => { out.extend_from_slice(&js[cursor as usize..pos as usize]); cursor = pos + size; } RewriteType::Replace { start, end, str } => { out.extend_from_slice(&js[cursor as usize..start as usize]); out.extend_from_slice(&str); cursor = end; } } } out.extend_from_slice(&js[cursor as usize..]); out.to_vec() } }