From 41b21eb4e4275326a87399baa4a40fc662e407f7 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Fri, 27 Jun 2025 18:33:50 +0200 Subject: [PATCH 01/11] fix: java 1.8 version in maven files --- minify-html-java/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/minify-html-java/pom.xml b/minify-html-java/pom.xml index 98cbec62..9ce5546d 100644 --- a/minify-html-java/pom.xml +++ b/minify-html-java/pom.xml @@ -45,7 +45,7 @@ - 1.7 + 1.8 UTF-8 UTF-8 @@ -57,8 +57,8 @@ maven-compiler-plugin 3.8.1 - 1.7 - 1.7 + ${java.version} + ${java.version} From 1f9e2334953f761d26213ee1d0c4efb35daa3116 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Fri, 27 Jun 2025 18:41:27 +0200 Subject: [PATCH 02/11] feat: use lombok to generate java classes Signed-off-by: jogerj <[email protected]> --- minify-html-java/lombok.config | 2 + minify-html-java/pom.xml | 17 ++ .../in/wilsonl/minifyhtml/Configuration.java | 176 +++--------------- .../minifyhtml/Configuration.java.gen.js | 53 ------ .../in/wilsonl/minifyhtml/MinifyHtml.java | 52 +++--- minify-html-java/src/main/rust/lib.rs | 30 +-- 6 files changed, 88 insertions(+), 242 deletions(-) create mode 100644 minify-html-java/lombok.config delete mode 100644 minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java.gen.js diff --git a/minify-html-java/lombok.config b/minify-html-java/lombok.config new file mode 100644 index 00000000..48d40f82 --- /dev/null +++ b/minify-html-java/lombok.config @@ -0,0 +1,2 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = false diff --git a/minify-html-java/pom.xml b/minify-html-java/pom.xml index 9ce5546d..9f93302e 100644 --- a/minify-html-java/pom.xml +++ b/minify-html-java/pom.xml @@ -46,10 +46,20 @@ 1.8 + 1.18.38 UTF-8 UTF-8 + + + org.projectlombok + lombok + ${lombok.version} + provided + + + @@ -57,6 +67,13 @@ maven-compiler-plugin 3.8.1 + + + org.projectlombok + lombok + ${lombok.version} + + ${java.version} ${java.version} diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java index c06181da..512a384e 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java @@ -1,160 +1,32 @@ package in.wilsonl.minifyhtml; -// WARNING: Do not manually edit, use Configuration.java.gen.js. +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; /** * Class representing minification configuration. + * Use the {@link Builder} to create an instance of this class. */ +@Builder( + setterPrefix = "set", + builderClassName = "Builder" +) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class Configuration { - public final boolean allow_noncompliant_unquoted_attribute_values; - public final boolean allow_optimal_entities; - public final boolean allow_removing_spaces_between_attributes; - public final boolean keep_closing_tags; - public final boolean keep_comments; - public final boolean keep_html_and_head_opening_tags; - public final boolean keep_input_type_text_attr; - public final boolean keep_ssi_comments; - public final boolean minify_css; - public final boolean minify_doctype; - public final boolean minify_js; - public final boolean preserve_brace_template_syntax; - public final boolean preserve_chevron_percent_template_syntax; - public final boolean remove_bangs; - public final boolean remove_processing_instructions; - - private Configuration( - boolean allow_noncompliant_unquoted_attribute_values, - boolean allow_optimal_entities, - boolean allow_removing_spaces_between_attributes, - boolean keep_closing_tags, - boolean keep_comments, - boolean keep_html_and_head_opening_tags, - boolean keep_input_type_text_attr, - boolean keep_ssi_comments, - boolean minify_css, - boolean minify_doctype, - boolean minify_js, - boolean preserve_brace_template_syntax, - boolean preserve_chevron_percent_template_syntax, - boolean remove_bangs, - boolean remove_processing_instructions - ) { - this.allow_noncompliant_unquoted_attribute_values = allow_noncompliant_unquoted_attribute_values; - this.allow_optimal_entities = allow_optimal_entities; - this.allow_removing_spaces_between_attributes = allow_removing_spaces_between_attributes; - this.keep_closing_tags = keep_closing_tags; - this.keep_comments = keep_comments; - this.keep_html_and_head_opening_tags = keep_html_and_head_opening_tags; - this.keep_input_type_text_attr = keep_input_type_text_attr; - this.keep_ssi_comments = keep_ssi_comments; - this.minify_css = minify_css; - this.minify_doctype = minify_doctype; - this.minify_js = minify_js; - this.preserve_brace_template_syntax = preserve_brace_template_syntax; - this.preserve_chevron_percent_template_syntax = preserve_chevron_percent_template_syntax; - this.remove_bangs = remove_bangs; - this.remove_processing_instructions = remove_processing_instructions; - } - - /** - * Builder to help create configuration. - */ - public static class Builder { - private boolean allow_noncompliant_unquoted_attribute_values = false; - private boolean allow_optimal_entities = false; - private boolean allow_removing_spaces_between_attributes = false; - private boolean keep_closing_tags = false; - private boolean keep_comments = false; - private boolean keep_html_and_head_opening_tags = false; - private boolean keep_input_type_text_attr = false; - private boolean keep_ssi_comments = false; - private boolean minify_css = false; - private boolean minify_doctype = false; - private boolean minify_js = false; - private boolean preserve_brace_template_syntax = false; - private boolean preserve_chevron_percent_template_syntax = false; - private boolean remove_bangs = false; - private boolean remove_processing_instructions = false; - - public Builder setAllowNoncompliantUnquotedAttributeValues(boolean v) { - this.allow_noncompliant_unquoted_attribute_values = v; - return this; - } - public Builder setAllowOptimalEntities(boolean v) { - this.allow_optimal_entities = v; - return this; - } - public Builder setAllowRemovingSpacesBetweenAttributes(boolean v) { - this.allow_removing_spaces_between_attributes = v; - return this; - } - public Builder setKeepClosingTags(boolean v) { - this.keep_closing_tags = v; - return this; - } - public Builder setKeepComments(boolean v) { - this.keep_comments = v; - return this; - } - public Builder setKeepHtmlAndHeadOpeningTags(boolean v) { - this.keep_html_and_head_opening_tags = v; - return this; - } - public Builder setKeepInputTypeTextAttr(boolean v) { - this.keep_input_type_text_attr = v; - return this; - } - public Builder setKeepSsiComments(boolean v) { - this.keep_ssi_comments = v; - return this; - } - public Builder setMinifyCss(boolean v) { - this.minify_css = v; - return this; - } - public Builder setMinifyDoctype(boolean v) { - this.minify_doctype = v; - return this; - } - public Builder setMinifyJs(boolean v) { - this.minify_js = v; - return this; - } - public Builder setPreserveBraceTemplateSyntax(boolean v) { - this.preserve_brace_template_syntax = v; - return this; - } - public Builder setPreserveChevronPercentTemplateSyntax(boolean v) { - this.preserve_chevron_percent_template_syntax = v; - return this; - } - public Builder setRemoveBangs(boolean v) { - this.remove_bangs = v; - return this; - } - public Builder setRemoveProcessingInstructions(boolean v) { - this.remove_processing_instructions = v; - return this; - } - - public Configuration build() { - return new Configuration( - this.allow_noncompliant_unquoted_attribute_values, - this.allow_optimal_entities, - this.allow_removing_spaces_between_attributes, - this.keep_closing_tags, - this.keep_comments, - this.keep_html_and_head_opening_tags, - this.keep_input_type_text_attr, - this.keep_ssi_comments, - this.minify_css, - this.minify_doctype, - this.minify_js, - this.preserve_brace_template_syntax, - this.preserve_chevron_percent_template_syntax, - this.remove_bangs, - this.remove_processing_instructions - ); - } - } + public final boolean allowNoncompliantUnquotedAttributeValues; + public final boolean allowOptimalEntities; + public final boolean allowRemovingSpacesBetweenAttributes; + public final boolean keepClosingTags; + public final boolean keepComments; + public final boolean keepHtmlAndHeadOpeningTags; + public final boolean keepInputTypeTextAttr; + public final boolean keepSsiComments; + public final boolean minifyCss; + public final boolean minifyDoctype; + public final boolean minifyJs; + public final boolean preserveBraceTemplateSyntax; + public final boolean preserveChevronPercentTemplateSyntax; + public final boolean removeBangs; + public final boolean removeProcessingInstructions; } diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java.gen.js b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java.gen.js deleted file mode 100644 index c76e5d04..00000000 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java.gen.js +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env node - -/* - Normally I'd avoid scripts to generate code as it makes it harder to reason and work/develop with. But in this case the Java class is too tedious and error-prone to do manually. -*/ - -const fs = require("fs"); - -const cfgRs = fs.readFileSync(`${__dirname}/../../../../../../../minify-html/src/cfg/mod.rs`, "utf8"); -const opts = []; -for (const [_, snake] of cfgRs.matchAll(/^\s*pub ([a-zA-Z0-9_]+): bool,?\s*$/gm)) { - const pascal = snake.split("_").map((w) => `${w[0].toUpperCase()}${w.slice(1)}`).join(""); - opts.push({snake, pascal}); -} - -const java = ` -package in.wilsonl.minifyhtml; - -// WARNING: Do not manually edit, use Configuration.java.gen.js. - -/** - * Class representing minification configuration. - */ -public class Configuration { - ${opts.map(o => `public final boolean ${o.snake};`).join("\n ")} - - private Configuration( - ${opts.map(o => `boolean ${o.snake}`).join(",\n ")} - ) { - ${opts.map(o => `this.${o.snake} = ${o.snake};`).join("\n ")} - } - - /** - * Builder to help create configuration. - */ - public static class Builder { - ${opts.map(o => `private boolean ${o.snake} = false;`).join("\n ")} - - ${opts.map(o => `public Builder set${o.pascal}(boolean v) { - this.${o.snake} = v; - return this; - }`).join("\n ")} - - public Configuration build() { - return new Configuration( - ${opts.map(o => `this.${o.snake}`).join(",\n ")} - ); - } - } -} -`.trim(); - -fs.writeFileSync(`${__dirname}/Configuration.java`, java); diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java index 50bf0481..61b2c829 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java @@ -2,12 +2,9 @@ import java.io.File; import java.io.InputStream; -import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import static java.lang.String.format; - /** * Class containing only static methods and exception classes. Cannot be instantiated. * Methods call to native compiled Rust code using JNI. @@ -15,31 +12,20 @@ */ public class MinifyHtml { static { - String osName = System.getProperty("os.name").toLowerCase(); - String osArch = System.getProperty("os.arch").toLowerCase(); - - String nativeLibNameOs = osName.startsWith("windows") - ? "win" - : osName.startsWith("linux") - ? "linux" - : osName.startsWith("mac") - ? "mac" - : null; - String nativeLibNameArch = - osArch.equals("amd64") || osArch.equals("x86_64") - ? "x64" - : osArch.equals("arm64") || osArch.equals("aarch64") - ? "aarch64" - : null; + final String osName = System.getProperty("os.name").toLowerCase(); + final String osArch = System.getProperty("os.arch").toLowerCase(); + + final String nativeLibNameOs = getNativeLibNameOs(osName); + final String nativeLibNameArch = getNativeLibNameArch(osArch); if (nativeLibNameOs == null || nativeLibNameArch == null) { - throw new RuntimeException(format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch)); + throw new RuntimeException(String.format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch)); } - String nativeLibFile = format("/%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); + final String nativeLibFile = String.format("/%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); try (InputStream is = MinifyHtml.class.getResourceAsStream(nativeLibFile)) { - File temp = File.createTempFile("minify-html-java-nativelib", nativeLibFile.substring(1)); + final File temp = File.createTempFile("minify-html-java-nativelib", nativeLibFile.substring(1)); temp.deleteOnExit(); Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); System.load(temp.getAbsolutePath()); @@ -60,4 +46,26 @@ private MinifyHtml() { * @return minified HTML code */ public static native String minify(String code, Configuration cfg); + + private static String getNativeLibNameOs(String osName) { + if (osName.startsWith("windows")) { + return "win"; + } else if (osName.startsWith("linux")) { + return "linux"; + } else if (osName.startsWith("mac")) { + return "mac"; + } else { + return null; + } + } + + private static String getNativeLibNameArch(String osArch) { + if (osArch.equals("amd64") || osArch.equals("x86_64")) { + return "x64"; + } else if (osArch.equals("arm64") || osArch.equals("aarch64")) { + return "aarch64"; + } else { + return null; + } + } } diff --git a/minify-html-java/src/main/rust/lib.rs b/minify-html-java/src/main/rust/lib.rs index ead6d2b0..698f6521 100644 --- a/minify-html-java/src/main/rust/lib.rs +++ b/minify-html-java/src/main/rust/lib.rs @@ -11,21 +11,21 @@ fn build_cfg(env: &JNIEnv, obj: &JObject) -> Cfg { #[rustfmt::skip] // This is a statement because "attributes on expressions are experimental". let cfg = Cfg { - allow_noncompliant_unquoted_attribute_values: env.get_field(*obj, "allow_noncompliant_unquoted_attribute_values", "Z").unwrap().z().unwrap(), - allow_optimal_entities: env.get_field(*obj, "allow_optimal_entities", "Z").unwrap().z().unwrap(), - allow_removing_spaces_between_attributes: env.get_field(*obj, "allow_removing_spaces_between_attributes", "Z").unwrap().z().unwrap(), - keep_closing_tags: env.get_field(*obj, "keep_closing_tags", "Z").unwrap().z().unwrap(), - keep_comments: env.get_field(*obj, "keep_comments", "Z").unwrap().z().unwrap(), - keep_html_and_head_opening_tags: env.get_field(*obj, "keep_html_and_head_opening_tags", "Z").unwrap().z().unwrap(), - keep_input_type_text_attr: env.get_field(*obj, "keep_input_type_text_attr", "Z").unwrap().z().unwrap(), - keep_ssi_comments: env.get_field(*obj, "keep_ssi_comments", "Z").unwrap().z().unwrap(), - minify_css: env.get_field(*obj, "minify_css", "Z").unwrap().z().unwrap(), - minify_doctype: env.get_field(*obj, "minify_doctype", "Z").unwrap().z().unwrap(), - minify_js: env.get_field(*obj, "minify_js", "Z").unwrap().z().unwrap(), - preserve_brace_template_syntax: env.get_field(*obj, "preserve_brace_template_syntax", "Z").unwrap().z().unwrap(), - preserve_chevron_percent_template_syntax: env.get_field(*obj, "preserve_chevron_percent_template_syntax", "Z").unwrap().z().unwrap(), - remove_bangs: env.get_field(*obj, "remove_bangs", "Z").unwrap().z().unwrap(), - remove_processing_instructions: env.get_field(*obj, "remove_processing_instructions", "Z").unwrap().z().unwrap(), + allow_noncompliant_unquoted_attribute_values: env.get_field(*obj, "allowNoncompliantUnquotedAttributeValues", "Z").unwrap().z().unwrap(), + allow_optimal_entities: env.get_field(*obj, "allowOptimalEntities", "Z").unwrap().z().unwrap(), + allow_removing_spaces_between_attributes: env.get_field(*obj, "allowRemovingSpacesBetweenAttributes", "Z").unwrap().z().unwrap(), + keep_closing_tags: env.get_field(*obj, "keepClosingTags", "Z").unwrap().z().unwrap(), + keep_comments: env.get_field(*obj, "keepComments", "Z").unwrap().z().unwrap(), + keep_html_and_head_opening_tags: env.get_field(*obj, "keepHtmlAndHeadOpeningTags", "Z").unwrap().z().unwrap(), + keep_input_type_text_attr: env.get_field(*obj, "keepInputTypeTextAttr", "Z").unwrap().z().unwrap(), + keep_ssi_comments: env.get_field(*obj, "keepSsiComments", "Z").unwrap().z().unwrap(), + minify_css: env.get_field(*obj, "minifyCss", "Z").unwrap().z().unwrap(), + minify_doctype: env.get_field(*obj, "minifyDoctype", "Z").unwrap().z().unwrap(), + minify_js: env.get_field(*obj, "minifyJs", "Z").unwrap().z().unwrap(), + preserve_brace_template_syntax: env.get_field(*obj, "preserveBraceTemplateSyntax", "Z").unwrap().z().unwrap(), + preserve_chevron_percent_template_syntax: env.get_field(*obj, "preserveChevronPercentTemplateSyntax", "Z").unwrap().z().unwrap(), + remove_bangs: env.get_field(*obj, "removeBangs", "Z").unwrap().z().unwrap(), + remove_processing_instructions: env.get_field(*obj, "removeProcessingInstructions", "Z").unwrap().z().unwrap(), }; cfg } From 137ba670713ef457bbb2210b277977a0a7443842 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 02:07:27 +0200 Subject: [PATCH 03/11] feat: use a value class for config Signed-off-by: jogerj <[email protected]> --- .../in/wilsonl/minifyhtml/Configuration.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java index 512a384e..d46fec9f 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java @@ -3,30 +3,32 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; +import lombok.Value; /** * Class representing minification configuration. * Use the {@link Builder} to create an instance of this class. */ +@AllArgsConstructor(access = AccessLevel.PRIVATE) @Builder( - setterPrefix = "set", - builderClassName = "Builder" + setterPrefix = "set", + builderClassName = "Builder" ) -@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Value public class Configuration { - public final boolean allowNoncompliantUnquotedAttributeValues; - public final boolean allowOptimalEntities; - public final boolean allowRemovingSpacesBetweenAttributes; - public final boolean keepClosingTags; - public final boolean keepComments; - public final boolean keepHtmlAndHeadOpeningTags; - public final boolean keepInputTypeTextAttr; - public final boolean keepSsiComments; - public final boolean minifyCss; - public final boolean minifyDoctype; - public final boolean minifyJs; - public final boolean preserveBraceTemplateSyntax; - public final boolean preserveChevronPercentTemplateSyntax; - public final boolean removeBangs; - public final boolean removeProcessingInstructions; + boolean allowNoncompliantUnquotedAttributeValues; + boolean allowOptimalEntities; + boolean allowRemovingSpacesBetweenAttributes; + boolean keepClosingTags; + boolean keepComments; + boolean keepHtmlAndHeadOpeningTags; + boolean keepInputTypeTextAttr; + boolean keepSsiComments; + boolean minifyCss; + boolean minifyDoctype; + boolean minifyJs; + boolean preserveBraceTemplateSyntax; + boolean preserveChevronPercentTemplateSyntax; + boolean removeBangs; + boolean removeProcessingInstructions; } From fc63072ddd8bdf8901fbc9548ce540e152a8c8c6 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 02:08:50 +0200 Subject: [PATCH 04/11] feat: call getter methods of java Configuration object Signed-off-by: jogerj <[email protected]> --- minify-html-java/src/main/rust/lib.rs | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/minify-html-java/src/main/rust/lib.rs b/minify-html-java/src/main/rust/lib.rs index 698f6521..5285dac1 100644 --- a/minify-html-java/src/main/rust/lib.rs +++ b/minify-html-java/src/main/rust/lib.rs @@ -11,21 +11,21 @@ fn build_cfg(env: &JNIEnv, obj: &JObject) -> Cfg { #[rustfmt::skip] // This is a statement because "attributes on expressions are experimental". let cfg = Cfg { - allow_noncompliant_unquoted_attribute_values: env.get_field(*obj, "allowNoncompliantUnquotedAttributeValues", "Z").unwrap().z().unwrap(), - allow_optimal_entities: env.get_field(*obj, "allowOptimalEntities", "Z").unwrap().z().unwrap(), - allow_removing_spaces_between_attributes: env.get_field(*obj, "allowRemovingSpacesBetweenAttributes", "Z").unwrap().z().unwrap(), - keep_closing_tags: env.get_field(*obj, "keepClosingTags", "Z").unwrap().z().unwrap(), - keep_comments: env.get_field(*obj, "keepComments", "Z").unwrap().z().unwrap(), - keep_html_and_head_opening_tags: env.get_field(*obj, "keepHtmlAndHeadOpeningTags", "Z").unwrap().z().unwrap(), - keep_input_type_text_attr: env.get_field(*obj, "keepInputTypeTextAttr", "Z").unwrap().z().unwrap(), - keep_ssi_comments: env.get_field(*obj, "keepSsiComments", "Z").unwrap().z().unwrap(), - minify_css: env.get_field(*obj, "minifyCss", "Z").unwrap().z().unwrap(), - minify_doctype: env.get_field(*obj, "minifyDoctype", "Z").unwrap().z().unwrap(), - minify_js: env.get_field(*obj, "minifyJs", "Z").unwrap().z().unwrap(), - preserve_brace_template_syntax: env.get_field(*obj, "preserveBraceTemplateSyntax", "Z").unwrap().z().unwrap(), - preserve_chevron_percent_template_syntax: env.get_field(*obj, "preserveChevronPercentTemplateSyntax", "Z").unwrap().z().unwrap(), - remove_bangs: env.get_field(*obj, "removeBangs", "Z").unwrap().z().unwrap(), - remove_processing_instructions: env.get_field(*obj, "removeProcessingInstructions", "Z").unwrap().z().unwrap(), + allow_noncompliant_unquoted_attribute_values: env.call_method(*obj, "getAllowNoncompliantUnquotedAttributeValues", "()Z", &[]).unwrap().z().unwrap(), + allow_optimal_entities: env.call_method(*obj, "getAllowOptimalEntities", "()Z", &[]).unwrap().z().unwrap(), + allow_removing_spaces_between_attributes: env.call_method(*obj, "getAllowRemovingSpacesBetweenAttributes", "()Z", &[]).unwrap().z().unwrap(), + keep_closing_tags: env.call_method(*obj, "getKeepClosingTags", "()Z", &[]).unwrap().z().unwrap(), + keep_comments: env.call_method(*obj, "getKeepComments", "()Z", &[]).unwrap().z().unwrap(), + keep_html_and_head_opening_tags: env.call_method(*obj, "getKeepHtmlAndHeadOpeningTags", "()Z", &[]).unwrap().z().unwrap(), + keep_input_type_text_attr: env.call_method(*obj, "getKeepInputTypeTextAttr", "()Z", &[]).unwrap().z().unwrap(), + keep_ssi_comments: env.call_method(*obj, "getKeepSsiComments", "()Z", &[]).unwrap().z().unwrap(), + minify_css: env.call_method(*obj, "getMinifyCss", "()Z", &[]).unwrap().z().unwrap(), + minify_doctype: env.call_method(*obj, "getMinifyDoctype", "()Z", &[]).unwrap().z().unwrap(), + minify_js: env.call_method(*obj, "getMinifyJs", "()Z", &[]).unwrap().z().unwrap(), + preserve_brace_template_syntax: env.call_method(*obj, "getPreserveBraceTemplateSyntax", "()Z", &[]).unwrap().z().unwrap(), + preserve_chevron_percent_template_syntax: env.call_method(*obj, "getPreserveChevronPercentTemplateSyntax", "()Z", &[]).unwrap().z().unwrap(), + remove_bangs: env.call_method(*obj, "getRemoveBangs", "()Z", &[]).unwrap().z().unwrap(), + remove_processing_instructions: env.call_method(*obj, "getRemoveProcessingInstructions", "()Z", &[]).unwrap().z().unwrap(), }; cfg } From 56d17efb743b8ccff6156be1ba1dad07853d907e Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 02:10:26 +0200 Subject: [PATCH 05/11] feat: wrap native calls in null checks to prevent panic Signed-off-by: jogerj <[email protected]> --- .../in/wilsonl/minifyhtml/MinifyHtml.java | 23 ++++++++++++++----- minify-html-java/src/main/rust/lib.rs | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java index 61b2c829..b855f661 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java @@ -22,10 +22,10 @@ public class MinifyHtml { throw new RuntimeException(String.format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch)); } - final String nativeLibFile = String.format("/%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); + final String nativeLibFile = String.format("%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); - try (InputStream is = MinifyHtml.class.getResourceAsStream(nativeLibFile)) { - final File temp = File.createTempFile("minify-html-java-nativelib", nativeLibFile.substring(1)); + try (InputStream is = MinifyHtml.class.getResourceAsStream("/" + nativeLibFile)) { + final File temp = File.createTempFile("minify-html-java-nativelib", nativeLibFile); temp.deleteOnExit(); Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); System.load(temp.getAbsolutePath()); @@ -41,11 +41,22 @@ private MinifyHtml() { * Minify HTML code represented as a {@link String}. * The {@link String} will be copied to a UTF-8 byte array in native code, and then copied back into a Java {@link String}. * - * @param code HTML code to minify - * @param cfg {@link Configuration} minification settings to use + * @param code HTML code to minify, cannot be null + * @param cfg {@link Configuration} minification settings to use, cannot be null * @return minified HTML code + * @throws IllegalArgumentException if either {@code code} or {@code cfg} is null */ - public static native String minify(String code, Configuration cfg); + public static String minify(String code, Configuration cfg) { + if (code == null) { + throw new IllegalArgumentException("code cannot be null"); + } + if (cfg == null) { + throw new IllegalArgumentException("configuration cannot be null"); + } + return minifyRs(code, cfg); + } + + private static native String minifyRs(String code, Configuration cfg); private static String getNativeLibNameOs(String osName) { if (osName.startsWith("windows")) { diff --git a/minify-html-java/src/main/rust/lib.rs b/minify-html-java/src/main/rust/lib.rs index 5285dac1..5e88ccaf 100644 --- a/minify-html-java/src/main/rust/lib.rs +++ b/minify-html-java/src/main/rust/lib.rs @@ -31,7 +31,7 @@ fn build_cfg(env: &JNIEnv, obj: &JObject) -> Cfg { } #[no_mangle] -pub extern "system" fn Java_in_wilsonl_minifyhtml_MinifyHtml_minify( +pub extern "system" fn Java_in_wilsonl_minifyhtml_MinifyHtml_minifyRs( env: JNIEnv, _class: JClass, input: JString, From 57f74a4021ef890dd7edfb26ecd6e8aef4ab6af3 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 02:10:42 +0200 Subject: [PATCH 06/11] feat: add unit tests for sanity check Signed-off-by: jogerj <[email protected]> --- minify-html-java/pom.xml | 13 ++++++++++ .../in/wilsonl/minifyhtml/MinifyHtmlTest.java | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java diff --git a/minify-html-java/pom.xml b/minify-html-java/pom.xml index 9f93302e..ef2f5ff8 100644 --- a/minify-html-java/pom.xml +++ b/minify-html-java/pom.xml @@ -46,6 +46,7 @@ 1.8 + 5.9.3 1.18.38 UTF-8 UTF-8 @@ -58,6 +59,12 @@ ${lombok.version} provided + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + @@ -110,6 +117,12 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + org.apache.maven.plugins maven-gpg-plugin diff --git a/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java b/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java new file mode 100644 index 00000000..9f52f3ab --- /dev/null +++ b/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java @@ -0,0 +1,26 @@ +package in.wilsonl.minifyhtml; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class MinifyHtmlTest { + + static final Configuration DEFAULT_CONFIG = new Configuration.Builder().build(); + + @Test + void testMinifyHtmlCallsNativeFunction() { + final String input = " Hello World! "; + final String expected = "Hello World!"; + assertEquals(expected, MinifyHtml.minify(input, DEFAULT_CONFIG), "Basic HTML minification failed"); + assertEquals("", MinifyHtml.minify("", DEFAULT_CONFIG)); + } + + @Test + void testNullParametersHandled() { + assertThrows(IllegalArgumentException.class, () -> MinifyHtml.minify(null, DEFAULT_CONFIG)); + assertThrows(IllegalArgumentException.class, () -> MinifyHtml.minify("", null)); + assertThrows(IllegalArgumentException.class, () -> MinifyHtml.minify(null, null)); + } +} From 39031acaa65db9f2e528beace5fbb9aa658e3396 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 02:25:28 +0200 Subject: [PATCH 07/11] feat: bump maven plugins version Signed-off-by: jogerj <[email protected]> --- minify-html-java/pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/minify-html-java/pom.xml b/minify-html-java/pom.xml index ef2f5ff8..3e8b8148 100644 --- a/minify-html-java/pom.xml +++ b/minify-html-java/pom.xml @@ -72,7 +72,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 @@ -89,7 +89,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.3.1 attach-sources @@ -103,7 +103,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 3.6.3 ${java.version} @@ -126,7 +126,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 3.2.6 sign-artifacts @@ -141,7 +141,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.14 true ossrh From c46af908aec9bceac245cd201208a78833fbe7b4 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 03:01:59 +0200 Subject: [PATCH 08/11] feat: script to generate java configs Signed-off-by: jogerj <[email protected]> --- .../in/wilsonl/minifyhtml/Configuration.java | 2 + minify-html-java/src/main/rust/lib.rs | 2 + minify-html-java/update-java.sh | 99 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100755 minify-html-java/update-java.sh diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java index d46fec9f..3e8602dc 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java @@ -16,6 +16,7 @@ ) @Value public class Configuration { + /* BEGIN FIELD NAMES */ boolean allowNoncompliantUnquotedAttributeValues; boolean allowOptimalEntities; boolean allowRemovingSpacesBetweenAttributes; @@ -31,4 +32,5 @@ public class Configuration { boolean preserveChevronPercentTemplateSyntax; boolean removeBangs; boolean removeProcessingInstructions; + /* END FIELD NAMES */ } diff --git a/minify-html-java/src/main/rust/lib.rs b/minify-html-java/src/main/rust/lib.rs index 5e88ccaf..c80ba3b8 100644 --- a/minify-html-java/src/main/rust/lib.rs +++ b/minify-html-java/src/main/rust/lib.rs @@ -11,6 +11,7 @@ fn build_cfg(env: &JNIEnv, obj: &JObject) -> Cfg { #[rustfmt::skip] // This is a statement because "attributes on expressions are experimental". let cfg = Cfg { + // BEGIN CONFIGURATION FIELDS allow_noncompliant_unquoted_attribute_values: env.call_method(*obj, "getAllowNoncompliantUnquotedAttributeValues", "()Z", &[]).unwrap().z().unwrap(), allow_optimal_entities: env.call_method(*obj, "getAllowOptimalEntities", "()Z", &[]).unwrap().z().unwrap(), allow_removing_spaces_between_attributes: env.call_method(*obj, "getAllowRemovingSpacesBetweenAttributes", "()Z", &[]).unwrap().z().unwrap(), @@ -26,6 +27,7 @@ fn build_cfg(env: &JNIEnv, obj: &JObject) -> Cfg { preserve_chevron_percent_template_syntax: env.call_method(*obj, "getPreserveChevronPercentTemplateSyntax", "()Z", &[]).unwrap().z().unwrap(), remove_bangs: env.call_method(*obj, "getRemoveBangs", "()Z", &[]).unwrap().z().unwrap(), remove_processing_instructions: env.call_method(*obj, "getRemoveProcessingInstructions", "()Z", &[]).unwrap().z().unwrap(), + // END CONFIGURATION FIELDS }; cfg } diff --git a/minify-html-java/update-java.sh b/minify-html-java/update-java.sh new file mode 100755 index 00000000..9dd677d0 --- /dev/null +++ b/minify-html-java/update-java.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +# Configuration paths +RUST_CFG_FILE="../minify-html/src/cfg/mod.rs" +JAVA_CONFIG_FILE="src/main/java/in/wilsonl/minifyhtml/Configuration.java" +RUST_LIB_FILE="src/main/rust/lib.rs" + +# Function to convert snake_case to camelCase +snake_to_camel() { + echo "$1" | sed -r 's/_([a-z])/\U\1/g' +} + +# Function to capitalize first letter +capitalize() { + echo "$(tr '[:lower:]' '[:upper:]' <<< ${1:0:1})${1:1}" +} + +echo "Extracting fields from Rust configuration..." + +# Extract field names using grep and sed +rust_fields=$(grep -oP '^\s*pub \K[a-zA-Z0-9_]+(?=: bool,?\s*$)' "$RUST_CFG_FILE") + +if [ -z "$rust_fields" ]; then + echo "No boolean fields found in $RUST_CFG_FILE" + exit 1 +fi + +echo "Found fields:" +while IFS= read -r field; do + echo " - $field" +done <<< "$rust_fields" + +# Generate Java fields +echo "Generating Java fields..." +java_fields="" +while IFS= read -r field; do + camel_field=$(snake_to_camel "$field") + java_fields="$java_fields boolean $camel_field;\n" +done <<< "$rust_fields" + +# Update Java configuration file between markers +echo "Updating Java configuration file..." +temp_java=$(mktemp) +awk -v fields="$java_fields" ' + /\/\* BEGIN FIELD NAMES \*\// { + print $0 + printf "%s", fields + skip=1 + next + } + /\/\* END FIELD NAMES \*\// { + print $0 + skip=0 + next + } + !skip { + print + } +' "$JAVA_CONFIG_FILE" > "$temp_java" +mv "$temp_java" "$JAVA_CONFIG_FILE" + +# Generate Rust mappings +echo "Generating Rust JNI mappings..." +rust_mappings="" + +while IFS= read -r field; do + camel_field=$(snake_to_camel "$field") + getter_name="get$(capitalize "$camel_field")" + + mapping=" $field: env.call_method(*obj, \"$getter_name\", \"()Z\", &[]).unwrap().z().unwrap()," + + rust_mappings="$rust_mappings$mapping\n" +done <<< "$rust_fields" + +# Update Rust lib file between markers +echo "Updating Rust library file..." +temp_rust=$(mktemp) +awk -v mappings="$rust_mappings" ' + /\/\/ BEGIN CONFIGURATION FIELDS/ { + print $0 + printf "%s", mappings + skip=1 + next + } + /\/\/ END CONFIGURATION FIELDS/ { + print $0 + skip=0 + next + } + !skip { + print + } +' "$RUST_LIB_FILE" > "$temp_rust" +mv "$temp_rust" "$RUST_LIB_FILE" + +echo "Code generation completed!" +echo "Updated files:" +echo " - $JAVA_CONFIG_FILE" +echo " - $RUST_LIB_FILE" From a650c227bd45fff89f5f540477eb5bc211a719f3 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 12:32:07 +0200 Subject: [PATCH 09/11] fix: applied google java codestyle the original code used 2-spaced indents and google java code seems to be most consistent to this. - refactored some functions and finalized class Signed-off-by: jogerj <[email protected]> --- .../in/wilsonl/minifyhtml/Configuration.java | 46 +++++++++---------- .../in/wilsonl/minifyhtml/MinifyHtml.java | 46 ++++++++++--------- .../in/wilsonl/minifyhtml/MinifyHtmlTest.java | 2 +- minify-html-java/update-java.sh | 2 +- 4 files changed, 50 insertions(+), 46 deletions(-) mode change 100755 => 100644 minify-html-java/update-java.sh diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java index 3e8602dc..26ea77fd 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/Configuration.java @@ -6,31 +6,31 @@ import lombok.Value; /** - * Class representing minification configuration. - * Use the {@link Builder} to create an instance of this class. + * Class representing minification configuration. Use the {@link Builder} to create an instance of + * this class. */ @AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder( - setterPrefix = "set", - builderClassName = "Builder" -) +@Builder(setterPrefix = "set", builderClassName = "Builder") @Value public class Configuration { - /* BEGIN FIELD NAMES */ - boolean allowNoncompliantUnquotedAttributeValues; - boolean allowOptimalEntities; - boolean allowRemovingSpacesBetweenAttributes; - boolean keepClosingTags; - boolean keepComments; - boolean keepHtmlAndHeadOpeningTags; - boolean keepInputTypeTextAttr; - boolean keepSsiComments; - boolean minifyCss; - boolean minifyDoctype; - boolean minifyJs; - boolean preserveBraceTemplateSyntax; - boolean preserveChevronPercentTemplateSyntax; - boolean removeBangs; - boolean removeProcessingInstructions; - /* END FIELD NAMES */ + // Do not change fields below or remove the markers. + // Use `update-java.sh` to generate config fields. Lombok takes care of the rest + + /* BEGIN FIELD NAMES */ + boolean allowNoncompliantUnquotedAttributeValues; + boolean allowOptimalEntities; + boolean allowRemovingSpacesBetweenAttributes; + boolean keepClosingTags; + boolean keepComments; + boolean keepHtmlAndHeadOpeningTags; + boolean keepInputTypeTextAttr; + boolean keepSsiComments; + boolean minifyCss; + boolean minifyDoctype; + boolean minifyJs; + boolean preserveBraceTemplateSyntax; + boolean preserveChevronPercentTemplateSyntax; + boolean removeBangs; + boolean removeProcessingInstructions; + /* END FIELD NAMES */ } diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java index b855f661..23bba536 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java @@ -6,11 +6,12 @@ import java.nio.file.StandardCopyOption; /** - * Class containing only static methods and exception classes. Cannot be instantiated. - * Methods call to native compiled Rust code using JNI. - * When this class is loaded, a static initialiser will attempt to load a prebuilt native library for the running operating system and architecture from the JAR. If it cannot, a {@link RuntimeException} will be thrown. + * Class containing only static methods and exception classes. Cannot be instantiated. Methods call + * to native compiled Rust code using JNI. When this class is loaded, a static initialiser will + * attempt to load a prebuilt native library for the running operating system and architecture from + * the JAR. If it cannot, a {@link RuntimeException} will be thrown. */ -public class MinifyHtml { +public final class MinifyHtml { static { final String osName = System.getProperty("os.name").toLowerCase(); final String osArch = System.getProperty("os.arch").toLowerCase(); @@ -19,13 +20,16 @@ public class MinifyHtml { final String nativeLibNameArch = getNativeLibNameArch(osArch); if (nativeLibNameOs == null || nativeLibNameArch == null) { - throw new RuntimeException(String.format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch)); + throw new RuntimeException( + String.format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch)); } - final String nativeLibFile = String.format("%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); + final String nativeLibFile = + String.format("/%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); - try (InputStream is = MinifyHtml.class.getResourceAsStream("/" + nativeLibFile)) { - final File temp = File.createTempFile("minify-html-java-nativelib", nativeLibFile); + try (InputStream is = MinifyHtml.class.getResourceAsStream(nativeLibFile)) { + final File temp = + File.createTempFile("minify-html-java-nativelib", nativeLibFile.substring(1)); temp.deleteOnExit(); Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); System.load(temp.getAbsolutePath()); @@ -34,15 +38,14 @@ public class MinifyHtml { } } - private MinifyHtml() { - } + private MinifyHtml() {} /** - * Minify HTML code represented as a {@link String}. - * The {@link String} will be copied to a UTF-8 byte array in native code, and then copied back into a Java {@link String}. + * Minify HTML code represented as a {@link String}. The {@link String} will be copied to a UTF-8 + * byte array in native code, and then copied back into a Java {@link String}. * * @param code HTML code to minify, cannot be null - * @param cfg {@link Configuration} minification settings to use, cannot be null + * @param cfg {@link Configuration} minification settings to use, cannot be null * @return minified HTML code * @throws IllegalArgumentException if either {@code code} or {@code cfg} is null */ @@ -59,24 +62,25 @@ public static String minify(String code, Configuration cfg) { private static native String minifyRs(String code, Configuration cfg); private static String getNativeLibNameOs(String osName) { + if (osName.startsWith("linux")) { + return "linux"; + } if (osName.startsWith("windows")) { return "win"; - } else if (osName.startsWith("linux")) { - return "linux"; - } else if (osName.startsWith("mac")) { + } + if (osName.startsWith("mac")) { return "mac"; - } else { - return null; } + return null; } private static String getNativeLibNameArch(String osArch) { if (osArch.equals("amd64") || osArch.equals("x86_64")) { return "x64"; - } else if (osArch.equals("arm64") || osArch.equals("aarch64")) { + } + if (osArch.equals("arm64") || osArch.equals("aarch64")) { return "aarch64"; - } else { - return null; } + return null; } } diff --git a/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java b/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java index 9f52f3ab..f772bfc7 100644 --- a/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java +++ b/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java @@ -13,7 +13,7 @@ class MinifyHtmlTest { void testMinifyHtmlCallsNativeFunction() { final String input = " Hello World! "; final String expected = "Hello World!"; - assertEquals(expected, MinifyHtml.minify(input, DEFAULT_CONFIG), "Basic HTML minification failed"); + assertEquals(expected, MinifyHtml.minify(input, DEFAULT_CONFIG)); assertEquals("", MinifyHtml.minify("", DEFAULT_CONFIG)); } diff --git a/minify-html-java/update-java.sh b/minify-html-java/update-java.sh old mode 100755 new mode 100644 index 9dd677d0..ff3718de --- a/minify-html-java/update-java.sh +++ b/minify-html-java/update-java.sh @@ -35,7 +35,7 @@ echo "Generating Java fields..." java_fields="" while IFS= read -r field; do camel_field=$(snake_to_camel "$field") - java_fields="$java_fields boolean $camel_field;\n" + java_fields="$java_fields boolean $camel_field;\n" done <<< "$rust_fields" # Update Java configuration file between markers From db75854ac66e86834b3b973b4517ac16251e3546 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 12:48:31 +0200 Subject: [PATCH 10/11] feat: own exceptions to replace generic ones Signed-off-by: jogerj <[email protected]> --- .../in/wilsonl/minifyhtml/MinifyHtml.java | 37 ++++++++++--------- .../minifyhtml/MinifyHtmlException.java | 11 ++++++ 2 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtmlException.java diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java index 23bba536..4eee3127 100644 --- a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtml.java @@ -13,28 +13,15 @@ */ public final class MinifyHtml { static { - final String osName = System.getProperty("os.name").toLowerCase(); - final String osArch = System.getProperty("os.arch").toLowerCase(); - - final String nativeLibNameOs = getNativeLibNameOs(osName); - final String nativeLibNameArch = getNativeLibNameArch(osArch); - - if (nativeLibNameOs == null || nativeLibNameArch == null) { - throw new RuntimeException( - String.format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch)); - } - - final String nativeLibFile = - String.format("/%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); - - try (InputStream is = MinifyHtml.class.getResourceAsStream(nativeLibFile)) { - final File temp = - File.createTempFile("minify-html-java-nativelib", nativeLibFile.substring(1)); + final String nativeLibFilePath = getNativeLibFilePath(); + try (InputStream is = MinifyHtml.class.getResourceAsStream(nativeLibFilePath)) { + final String nativeLibFileName = nativeLibFilePath.substring(1); + final File temp = File.createTempFile("minify-html-java-nativelib", nativeLibFileName); temp.deleteOnExit(); Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); System.load(temp.getAbsolutePath()); } catch (Exception e) { - throw new RuntimeException("Failed to load native library", e); + throw new MinifyHtmlException("Failed to load native library", e); } } @@ -61,6 +48,20 @@ public static String minify(String code, Configuration cfg) { private static native String minifyRs(String code, Configuration cfg); + private static String getNativeLibFilePath() { + final String osName = System.getProperty("os.name").toLowerCase(); + final String osArch = System.getProperty("os.arch").toLowerCase(); + + final String nativeLibNameOs = getNativeLibNameOs(osName); + final String nativeLibNameArch = getNativeLibNameArch(osArch); + + if (nativeLibNameOs == null || nativeLibNameArch == null) { + throw new MinifyHtmlException( + String.format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch)); + } + return String.format("/%s-%s.nativelib", nativeLibNameOs, nativeLibNameArch); + } + private static String getNativeLibNameOs(String osName) { if (osName.startsWith("linux")) { return "linux"; diff --git a/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtmlException.java b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtmlException.java new file mode 100644 index 00000000..6703b905 --- /dev/null +++ b/minify-html-java/src/main/java/in/wilsonl/minifyhtml/MinifyHtmlException.java @@ -0,0 +1,11 @@ +package in.wilsonl.minifyhtml; + +public class MinifyHtmlException extends RuntimeException { + MinifyHtmlException(String message) { + super(message); + } + + MinifyHtmlException(String message, Throwable cause) { + super(message, cause); + } +} From 3e3e4353b37ff1605019268e67f52e431a236ab2 Mon Sep 17 00:00:00 2001 From: jogerj <[email protected]> Date: Sat, 28 Jun 2025 14:01:28 +0200 Subject: [PATCH 11/11] fix: wrong getter prefix for boolean added tests to prevent this Signed-off-by: jogerj <[email protected]> --- minify-html-java/src/main/rust/lib.rs | 30 +++++------ .../in/wilsonl/minifyhtml/MinifyHtmlTest.java | 50 ++++++++++++++++++- minify-html-java/update-java.sh | 2 +- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/minify-html-java/src/main/rust/lib.rs b/minify-html-java/src/main/rust/lib.rs index c80ba3b8..2133c303 100644 --- a/minify-html-java/src/main/rust/lib.rs +++ b/minify-html-java/src/main/rust/lib.rs @@ -12,21 +12,21 @@ fn build_cfg(env: &JNIEnv, obj: &JObject) -> Cfg { // This is a statement because "attributes on expressions are experimental". let cfg = Cfg { // BEGIN CONFIGURATION FIELDS - allow_noncompliant_unquoted_attribute_values: env.call_method(*obj, "getAllowNoncompliantUnquotedAttributeValues", "()Z", &[]).unwrap().z().unwrap(), - allow_optimal_entities: env.call_method(*obj, "getAllowOptimalEntities", "()Z", &[]).unwrap().z().unwrap(), - allow_removing_spaces_between_attributes: env.call_method(*obj, "getAllowRemovingSpacesBetweenAttributes", "()Z", &[]).unwrap().z().unwrap(), - keep_closing_tags: env.call_method(*obj, "getKeepClosingTags", "()Z", &[]).unwrap().z().unwrap(), - keep_comments: env.call_method(*obj, "getKeepComments", "()Z", &[]).unwrap().z().unwrap(), - keep_html_and_head_opening_tags: env.call_method(*obj, "getKeepHtmlAndHeadOpeningTags", "()Z", &[]).unwrap().z().unwrap(), - keep_input_type_text_attr: env.call_method(*obj, "getKeepInputTypeTextAttr", "()Z", &[]).unwrap().z().unwrap(), - keep_ssi_comments: env.call_method(*obj, "getKeepSsiComments", "()Z", &[]).unwrap().z().unwrap(), - minify_css: env.call_method(*obj, "getMinifyCss", "()Z", &[]).unwrap().z().unwrap(), - minify_doctype: env.call_method(*obj, "getMinifyDoctype", "()Z", &[]).unwrap().z().unwrap(), - minify_js: env.call_method(*obj, "getMinifyJs", "()Z", &[]).unwrap().z().unwrap(), - preserve_brace_template_syntax: env.call_method(*obj, "getPreserveBraceTemplateSyntax", "()Z", &[]).unwrap().z().unwrap(), - preserve_chevron_percent_template_syntax: env.call_method(*obj, "getPreserveChevronPercentTemplateSyntax", "()Z", &[]).unwrap().z().unwrap(), - remove_bangs: env.call_method(*obj, "getRemoveBangs", "()Z", &[]).unwrap().z().unwrap(), - remove_processing_instructions: env.call_method(*obj, "getRemoveProcessingInstructions", "()Z", &[]).unwrap().z().unwrap(), + allow_noncompliant_unquoted_attribute_values: env.call_method(*obj, "isAllowNoncompliantUnquotedAttributeValues", "()Z", &[]).unwrap().z().unwrap(), + allow_optimal_entities: env.call_method(*obj, "isAllowOptimalEntities", "()Z", &[]).unwrap().z().unwrap(), + allow_removing_spaces_between_attributes: env.call_method(*obj, "isAllowRemovingSpacesBetweenAttributes", "()Z", &[]).unwrap().z().unwrap(), + keep_closing_tags: env.call_method(*obj, "isKeepClosingTags", "()Z", &[]).unwrap().z().unwrap(), + keep_comments: env.call_method(*obj, "isKeepComments", "()Z", &[]).unwrap().z().unwrap(), + keep_html_and_head_opening_tags: env.call_method(*obj, "isKeepHtmlAndHeadOpeningTags", "()Z", &[]).unwrap().z().unwrap(), + keep_input_type_text_attr: env.call_method(*obj, "isKeepInputTypeTextAttr", "()Z", &[]).unwrap().z().unwrap(), + keep_ssi_comments: env.call_method(*obj, "isKeepSsiComments", "()Z", &[]).unwrap().z().unwrap(), + minify_css: env.call_method(*obj, "isMinifyCss", "()Z", &[]).unwrap().z().unwrap(), + minify_doctype: env.call_method(*obj, "isMinifyDoctype", "()Z", &[]).unwrap().z().unwrap(), + minify_js: env.call_method(*obj, "isMinifyJs", "()Z", &[]).unwrap().z().unwrap(), + preserve_brace_template_syntax: env.call_method(*obj, "isPreserveBraceTemplateSyntax", "()Z", &[]).unwrap().z().unwrap(), + preserve_chevron_percent_template_syntax: env.call_method(*obj, "isPreserveChevronPercentTemplateSyntax", "()Z", &[]).unwrap().z().unwrap(), + remove_bangs: env.call_method(*obj, "isRemoveBangs", "()Z", &[]).unwrap().z().unwrap(), + remove_processing_instructions: env.call_method(*obj, "isRemoveProcessingInstructions", "()Z", &[]).unwrap().z().unwrap(), // END CONFIGURATION FIELDS }; cfg diff --git a/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java b/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java index f772bfc7..f6fef86d 100644 --- a/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java +++ b/minify-html-java/src/test/java/in/wilsonl/minifyhtml/MinifyHtmlTest.java @@ -1,9 +1,11 @@ package in.wilsonl.minifyhtml; +import java.lang.reflect.Method; +import lombok.val; import org.junit.jupiter.api.Test; +import org.junit.platform.commons.util.ReflectionUtils; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; class MinifyHtmlTest { @@ -23,4 +25,48 @@ void testNullParametersHandled() { assertThrows(IllegalArgumentException.class, () -> MinifyHtml.minify("", null)); assertThrows(IllegalArgumentException.class, () -> MinifyHtml.minify(null, null)); } + + @Test + void testAllConfigsEnabled() { + // dynamically find all config flags in Configuration.Builder + val configSetters = + ReflectionUtils.findMethods(Configuration.Builder.class, this::isBooleanSetter); + val configBuilder = new Configuration.Builder(); + // enable all boolean configuration options + assertDoesNotThrow( + () -> { + for (val method : configSetters) { + method.invoke(configBuilder, true); + } + }); + + val config = configBuilder.build(); + + val configGetters = ReflectionUtils.findMethods(Configuration.class, this::isBooleanGetter); + // verify all boolean configuration options are enabled + assertDoesNotThrow( + () -> { + for (val method : configGetters) { + assertTrue( + (boolean) method.invoke(config), + "Configuration option " + method.getName() + " should be true"); + } + }); + + final String input = " Hello World! "; + final String expected = "Hello World!"; + assertEquals(expected, MinifyHtml.minify(input, config)); + } + + private boolean isBooleanSetter(Method method) { + return method.getName().startsWith("set") + && method.getParameterCount() == 1 + && method.getParameterTypes()[0] == boolean.class; + } + + private boolean isBooleanGetter(Method method) { + return method.getName().startsWith("is") + && method.getParameterCount() == 0 + && method.getReturnType() == boolean.class; + } } diff --git a/minify-html-java/update-java.sh b/minify-html-java/update-java.sh index ff3718de..bbc8d2e1 100644 --- a/minify-html-java/update-java.sh +++ b/minify-html-java/update-java.sh @@ -65,7 +65,7 @@ rust_mappings="" while IFS= read -r field; do camel_field=$(snake_to_camel "$field") - getter_name="get$(capitalize "$camel_field")" + getter_name="is$(capitalize "$camel_field")" mapping=" $field: env.call_method(*obj, \"$getter_name\", \"()Z\", &[]).unwrap().z().unwrap(),"