diff --git a/bench/minifiers.js b/bench/minifiers.js
index 32acc15..5faf7d1 100644
--- a/bench/minifiers.js
+++ b/bench/minifiers.js
@@ -43,7 +43,7 @@ class EsbuildAsync {
async finalise (html) {
const jsTransformResults = await Promise.all(this.promises);
- return html.replace(/_____ESBUILD_ASYNC_PLACEHOLDER_([0-9]+)_____/g, (_, id) => jsTransformResults[id].code);
+ return html.replace(/_____ESBUILD_ASYNC_PLACEHOLDER_([0-9]+)_____/g, (_, id) => jsTransformResults[id].code.replace(/<\/script/g, "<\\/script"));
}
}
diff --git a/src/proc/mod.rs b/src/proc/mod.rs
index 5904fe1..f276ec5 100644
--- a/src/proc/mod.rs
+++ b/src/proc/mod.rs
@@ -3,21 +3,21 @@ use std::fmt::{Debug, Formatter};
use std::ops::{Index, IndexMut};
use aho_corasick::AhoCorasick;
-
-use crate::err::{Error, ErrorType, ProcessingResult, debug_repr};
-use crate::proc::MatchAction::*;
-use crate::proc::MatchMode::*;
-use crate::proc::range::ProcessorRange;
use memchr::memchr;
-use crate::gen::codepoints::Lookup;
#[cfg(feature = "js-esbuild")]
use {
- std::sync::{Arc, Mutex},
crossbeam::sync::WaitGroup,
esbuild_rs::TransformResult,
+ std::sync::{Arc, Mutex},
};
+use crate::err::{debug_repr, Error, ErrorType, ProcessingResult};
+use crate::gen::codepoints::Lookup;
+use crate::proc::MatchAction::*;
+use crate::proc::MatchMode::*;
+use crate::proc::range::ProcessorRange;
+
pub mod checkpoint;
pub mod entity;
pub mod range;
@@ -383,9 +383,17 @@ impl<'d> Processor<'d> {
let mut write_next = results.get(0).map_or(self.write_next, |r| r.src.start);
for (i, EsbuildSection { result, src }) in results.iter().enumerate() {
// Resulting minified JS/CSS to write.
- // TODO Verify.
- // TODO Handle potential `` in output code, which could be in string (e.g. orig. "" + "script>"), comment, or expression (e.g. orig. `a < /script>/.exec(b)?.length`).
- let min_code = result.code.as_str().trim();
+ // TODO Handle other forms:
+ // 1 < /script/.exec(a).length
+ // ` ${` ${a
+ // /*
+ // Considerations:
+ // - Need to parse strings (e.g. "", '', ``) so syntax within strings aren't mistakenly interpreted as code.
+ // - Need to be able to parse regex literals to determine string delimiters aren't actually characters in the regex.
+ // - Determining whether a slash is division or regex requires a full-blown JS parser to handle all cases (this is a well-known JS parsing problem).
+ // - `/let a=1;");
}
+#[cfg(feature = "js-esbuild")]
+#[test]
+fn test_js_minification_unintentional_closing_tag() {
+ eval_with_js_min(br#""#, br#""#);
+ eval_with_js_min(br#""#, br#""#);
+}
+
#[cfg(feature = "js-esbuild")]
#[test]
fn test_css_minification() {
diff --git a/src/unit/script.rs b/src/unit/script.rs
index 091da90..e519cf2 100644
--- a/src/unit/script.rs
+++ b/src/unit/script.rs
@@ -1,17 +1,19 @@
use aho_corasick::{AhoCorasick, AhoCorasickBuilder};
use lazy_static::lazy_static;
+
+#[cfg(feature = "js-esbuild")]
+use {
+ crate::proc::checkpoint::WriteCheckpoint,
+ crate::proc::EsbuildSection,
+ esbuild_rs::{TransformOptions, TransformOptionsBuilder},
+ std::sync::Arc,
+};
+
use crate::cfg::Cfg;
use crate::err::ProcessingResult;
use crate::proc::MatchAction::*;
use crate::proc::MatchMode::*;
use crate::proc::Processor;
-#[cfg(feature = "js-esbuild")]
-use {
- std::sync::Arc,
- esbuild_rs::{TransformOptionsBuilder, TransformOptions},
- crate::proc::EsbuildSection,
- crate::proc::checkpoint::WriteCheckpoint,
-};
#[cfg(feature = "js-esbuild")]
lazy_static! {
@@ -31,7 +33,7 @@ lazy_static! {
#[inline(always)]
pub fn process_script(proc: &mut Processor, cfg: &Cfg, js: bool) -> ProcessingResult<()> {
#[cfg(feature = "js-esbuild")]
- let start = WriteCheckpoint::new(proc);
+ let start = WriteCheckpoint::new(proc);
proc.require_not_at_end()?;
proc.m(WhileNotSeq(&SCRIPT_END), Keep);
// `process_tag` will require closing tag.