Skip to content

Instantly share code, notes, and snippets.

@JimmyRittenborg
Last active June 17, 2023 12:11
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save JimmyRittenborg/6f0bb74a996f9676ead5 to your computer and use it in GitHub Desktop.
Save JimmyRittenborg/6f0bb74a996f9676ead5 to your computer and use it in GitHub Desktop.
HTML Compressor in Shopify Liquid
{% comment %}
Input
{% endcomment %}{% capture _content %}{{ content }}{% endcapture %}{% comment %}
Remove redundant closing tags
{% endcomment %}{% assign _endings = "html head body li dt dd p rt rp optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% for _element in _endings %}
{% capture _end %}</{{ _element }}>{% endcapture %}
{% assign _content = _content | remove: _end %}
{% endfor %}{% comment %}
Create a variable to match newlines
{% endcomment %}{% capture nl %}
{% endcapture %}{% comment %}
Prepare inline <script>'s for compression
- We have to ensure a space after every </script> otherwise Liquid won't match, and blow up everything if two elements comes straight after eachother: <script></script><div></div>
{% endcomment %}{% assign _content = _content | replace: '</script>', '</script><!-- [SPACE] -->' | replace: '<!-- [SPACE] -->', " " %}{% assign _script_befores = _content | split: "<script" %}{% assign _content = "" %}{% for _script_before in _script_befores %}
{% assign _scripts = _script_before | split: "</script>" %}
{% case _scripts.size %}
{% when 2 %}
{% assign _script = _scripts.first |
replace: "// //", "/*[COMMENT]*/ " |
replace: "// //", "/*[COMMENT]*/ " |
replace: "// ", "/*[COMMENT]*/ " |
replace: " //", "/*[COMMENT]*/ " |
replace: " //", "/*[COMMENT]*/ " |
replace: "// ", "/*[COMMENT]*/ " |
replace: "//<![CDATA[", "/*[COMMENT]*/ " |
replace: "//]]>", "/*[COMMENT]*/ " |
%}
{% assign _script_comment_befores = _script | split: "/*[COMMENT]*/ " %}{% for _script_comment_before in _script_comment_befores %}
{% assign _script_comment_content = _script_comment_before | split: nl | first %}
{% if _script_comment_content %}
{% capture _script_comment %}{{ "/*[COMMENT]*/ " }}{{ _script_comment_content }}{{ nl }}{% endcapture %}
{% assign _script = _script | remove: _script_comment %}
{% endif %}
{% endfor %}
{% assign _script_comment_befores = _script | split: "/*" %}{% for _script_comment_before in _script_comment_befores %}
{% assign _script_comment_content = _script_comment_before | split: "*/" | first %}
{% if _script_comment_content %}
{% capture _script_comment %}{{ "/*" }}{{ _script_comment_content }}{{ "*/" }}{% endcapture %}
{% assign _script = _script | remove: _script_comment %}
{% endif %}
{% endfor %}
{% capture _content %}{{ _content }}<script {{ _script }}</script>{{ _scripts.last }}{% endcapture %}
{% when 1 %}
{% capture _content %}{{ _content }}{{ _scripts.last }}{% endcapture %}
{% endcase %}
{% endfor %}{% comment %}
Cleanup
- There is an 'missing semicolon' issue with Shopify's Enhanced Ecommerce for Google Analytics, which gets corrected here as well.
- There is an 'missing semicolon' issue with Shopify's inline Admin Panel script, which gets corrected here as well.
{% endcomment %}{% assign _content = _content | replace: '<script >', '<script>' | replace: '<script ', '<script ' | replace: 'analytics.onload = onload', ';analytics.onload = onload' | replace: 'analytics.async = true;', ';analytics.async = true;' | replace: 'if (window.addEventListener){ addEventListener("message", ', ';if (window.addEventListener){ addEventListener("message", ' %}{% comment %}
Compress whitespace outside of <pre>
- We have to ensure a space after every </pre> otherwise Liquid won't match, and blow up everything if two elements comes straight after eachother: <pre></pre><div></div>
{% endcomment %}{% assign _content = _content | replace: '</pre>', '</pre><!-- [SPACE] -->' | replace: '<!-- [SPACE] -->', " " %}{% assign _pre_befores = _content | split: "<pre" %}{% assign _content = "" %}{% for _pre_before in _pre_befores %}
{% assign _pres = _pre_before | split: "</pre>" %}
{% case _pres.size %}
{% when 2 %}
{% capture _content %}{{ _content }}<pre{{ _pres.first }}</pre>{{ _pres.last | split: " " | join: " " }}{% endcapture %}
{% when 1 %}
{% capture _content %}{{ _content }}{{ _pres.last | split: " " | join: " " }}{% endcapture %}
{% endcase %}
{% endfor %}{% comment %}
Remove html comments
- Matching just "<!--" and "-->" will whipe out IE Conditional Comments, which you probably don't want
{% endcomment %}{% assign _comment_befores = _content | split: "<!-- " %}{% for _comment_before in _comment_befores %}
{% assign _comment_content = _comment_before | split: " -->" | first %}
{% if _comment_content %}
{% capture _comment %}{{ "<!-- " }}{{ _comment_content }}{{ " -->" }}{% endcapture %}
{% assign _content = _content | remove: _comment %}
{% endif %}
{% endfor %}{% comment %}
Clip whitespace around elements
{% endcomment %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th option img script" | split: " " %}{% for _element in _clippings %}
{% assign _edges = " <e;<e; </e>;</e>;</e> ;</e>" | replace: "e", _element | split: ";" %}
{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}
{% endfor %}{% comment %}
Output
{% endcomment %}{{ _content }}
@DHL17
Copy link

DHL17 commented Sep 28, 2021

You can use it like this:
Create a snippit name 'compress'
{%- capture minified -%}
<!doctype html>
<!--all content--->
</html>
{%- endcapture -%}
{%- include 'compress', content: minified -%}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment