Skip to content

Commit

Permalink
【新增】网页压缩
Browse files Browse the repository at this point in the history
  • Loading branch information
PJ-568 committed Dec 16, 2024
1 parent b2f61fd commit 1587638
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
/Cargo.lock
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "markdown-html"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
minify-html = "0.15.0"
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!doctype html>

<html>
<markdown-html version="1.4.2" author="PJ568" repo="https://github.com/PJ-568/markdown.html"
<markdown-html version="1.5.0" author="PJ568" repo="https://github.com/PJ-568/markdown.html"
license="CC BY-SA 4.0 International"></markdown-html>

<head>
Expand Down
1 change: 1 addition & 0 deletions index.min.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!doctype html><html><markdown-html license="CC BY-SA 4.0 International" author=PJ568 repo=https://github.com/PJ-568/markdown.html version=1.5.0></markdown-html><head><meta charset=utf-8><meta content="HTML read markdown" name=description><meta content="width=256,initial-scale=1" name=viewport><script defer src=//lib.baomitu.com/marked/4.0.2/marked.min.js></script><script src=//lib.baomitu.com/pjax/0.2.8/pjax.min.js></script><script src=//lib.baomitu.com/translate.js/3.7.2/translate.js></script><script src=//lib.baomitu.com/Darkmode.js/1.5.7/darkmode-js.min.js></script><script>(()=>{let H=`.loading-bar`,K=`%`,O=`/`,L=`.markdown-body`,S=`</ul></li>`,N=`http://example.com`,Q=`README.md`,I=`.loading-bar .progress`,T=`,`,X=`#`,J=`loading`,E=null,F=1,M=`p`,P=`index.md`,R=``,U=`a[href\$=".md"]`,V=`href`,W=`click`,G=0,Y=`smooth`;var y=(()=>{if(f){document.querySelector(L).addEventListener(W,(async(a)=>{const b=a.target.closest(U);if(b){try{a.preventDefault();i();await v(await r(b.href))}catch(a){console.error(`自定义 PJAX 出错:`+ a)}}}))}});var n=(()=>{try{var a=translate.language.getCurrent();var b=translate.language.getLocal();if(a!==b){const a=document.querySelectorAll(`.translate-info`);if(a){a.forEach((a=>{a.remove()}))};var c=document.querySelector(L);var d=document.createElement(`blockquote`);d.classList.add(`translate-info`);var e=document.createElement(M);e.innerText=`当前页面已翻译。您可以:`;var f=document.createElement(M);var g=document.createElement(`a`);g.classList.add(`translate-switch`);g.href=`javascript:;`;g.title=`切换到原语言`;g.innerText=`切换到文章原本的语言`;d.appendChild(e);d.appendChild(g);c.insertBefore(d,c.children[F])}}catch(a){console.error(`检查并提示翻译出错:`+ a)}});var c=(async()=>await r(window.location.href));var s=(a=>{if(a){a=new URL(a,N).pathname;if(!a.endsWith(`.md`)){if(!a.endsWith(O)){a+=O};a+=P}};return a});var k=(()=>{try{const a=window.Pjax||(()=>{});d=new a({selectors:[`head title`,L,`.pjax-reload`],cacheBust:!1})}catch(a){console.error(`PJAX 初始化出错:`+ a)}});var z=((a=window.location.href)=>{if(f){if(g>h){history.pushState({url:a,title:document.title,uid:g},E,a);h=g++}else{h=g- F}}});var o=(()=>{try{translate.selectLanguageTag.refreshRender()}catch(a){console.error(`刷新翻译出错:`+ a)}});var l=(()=>{try{translate.service.use(`client.edge`);translate.request.listener.start();translate.setAutoDiscriminateLocalLanguage();translate.language.setUrlParamControl();translate.ignore.class.push(`notTranslate`);translate.execute()}catch(a){console.error(`翻译系统出错:`+ a)}});var p=(a=>{a=new URL(a,N).pathname;const b=a.lastIndexOf(O);return a.substring(G,b+ F)});var C=(()=>{m();B();A();l();k();x();y()});var j=(()=>{const b=document.querySelector(H);const c=document.querySelector(I);clearInterval(a);c.style.width=`100%`;b.classList.remove(J);setTimeout((()=>{c.style.width=G}),400)});var i=(()=>{const b=document.querySelector(H);const c=document.querySelector(I);var d=20;var e=95;b.classList.add(J);c.style.width=d+ K;clearInterval(a);a=setInterval((()=>{d+=3;if(d>e){d=e};c.style.width=d+ K}),500)});var B=(()=>{const a=document.querySelector(`.to-home`);a.href=window.location.pathname});var D=(()=>{o();n();translate.execute();j();window.scrollTo({top:G,behavior:Y})});var x=(()=>{d._handleResponse=d.handleResponse;d.handleResponse=(async(a,b,c,e)=>{if(b.responseText.startsWith(`<!doctype html><html><markdown-html`)||b.responseText.startsWith(`<!doctype html>\\n\\n<html>\\n<markdown-html`)){await v(await r(c))}else{d._handleResponse(a,b,c,e)}})});var u=(async(a)=>{document.title=`加载失败`;document.querySelector(L).innerHTML=`<h1>加载失败</h1><p>加载文档<code>${await c()}</code>时出错:<code>${a}</code>。您可以尝试<a class="refresh-btn" href="">重新加载</a>或<a class="back-btn">返回</a>。</p>`});var r=(async(a)=>{const b=new URL(a);const c=new URLSearchParams(b.search);a=s(c.get(M));return a});var m=(()=>{try{const a=window.Darkmode||(()=>{console.warn(`Darkmode.js 不存在?`);return});e=new a()}catch(a){console.error(`初始化 Darkmode.js 出错:`+ a)}});var a=E;var b=E;let d;let e;const f=history&&history.pushState;var g=F;var h=G;const q=p(window.location);const t=a=>!a.startsWith(O);const v=async(a,c=E)=>{if(a===E){var d=E;if(b){d=q+ Q}else if(b===!1){d=q+ P}else{c=await fetch(q+ P);if(c.ok){b=!1;d=q+ P}else{c=E;b=!0;d=q+ Q}}};try{const b=await w(a?a:d,c);document.querySelector(L).innerHTML=b;const e=document.querySelector(`h1`);document.title=e?e.textContent:(a?a:d);document.querySelector(`html`).classList.add(`loaded`);z(window.location.origin+ window.location.pathname+ (a?`?p=`+ a:R));D()}catch(a){u(a)}};const w=async(a,b=E)=>{var d=(a=>{var b=`<ul>`;const c=Array.from(a.querySelectorAll(`h1, h2, h3, h4, h5, h6`));var d=[];c.forEach(a=>{const c=parseInt(a.tagName.charAt(F));const e=a.textContent.toLowerCase().replace(/ /g,`-`).replace(/[^\w\u4e00-\u9fa5-]/g,R);a.setAttribute(`id`,e);while(d.length>G&&d[d.length- F].level>=c){d.pop();b+=S};if(d.length===G||d[d.length- F].level<c){b+=`<li><ul>`}else{b+=`</li><li>`};b+=`<a href="#${e}">${a.textContent}</a>`;d.push({level:c})});while(d.length>G){b+=S;d.pop()};b+=`</ul>`;return b});if(!b){b=await fetch(a);if(!b.ok){throw new Error(`请求失败:`+ a+ T+ b.status+ T+ b.statusText)}};var e=marked.parse(await b.text());e=`<details class="outline"><summary><code>${decodeURIComponent(a)}</code></summary></details><hr>${e}`;const f=new DOMParser();const g=f.parseFromString(e,`text/html`);const h=d(g);g.querySelector(`.outline`).innerHTML+=h;const i=g.querySelectorAll(U);const j=await c();i.forEach(b=>{var c=b.getAttribute(V);if(t(c)){if(j){c=p(a)+ c}else{c=q+ c};const d=new URL(c,N).pathname;b.setAttribute(V,`${window.location.pathname}?p=${d}`)}else if(c.startsWith(O)){b.setAttribute(V,`${window.location.pathname}?p=${c}`)}});return g.body.innerHTML};const A=async()=>{try{await v(await c())}catch(a){u(`初始化内容时出错:`,a)}};window.addEventListener(`DOMContentLoaded`,()=>C());document.addEventListener(`pjax:send`,(()=>{i()}));document.addEventListener(`pjax:complete`,(async()=>{await v(await c());y()}));document.addEventListener(W,(a=>{if(a.target.closest(`.back-btn`)){window.history.back()}else if(a.target.closest(`.refresh-btn`)){window.location.reload()}else if(a.target.closest(`.dark-btn`)){e.toggle()}else if(a.target.closest(`.translate-switch`)){translate.changeLanguage(translate.language.getLocal())}else if(a.target.closest(`.outline a[href]`)){const b=a.target.getAttribute(V);if(b!==X&&b.startsWith(X)){a.preventDefault();const c=document.querySelector(b);if(c){c.scrollIntoView({behavior:Y})}else{console.error(`ID 为 ${targetId} 的元素不存在。`)}}}}));window.onpopstate=(a=>{if(g>F){--g}})})()</script><title>加载中</title><style>*{font-family:Exo,sans-serif;text-decoration:none}:root{background-color:#e6e6e6}::selection{background-color:#dcedc8}body{margin:0}a{color:#66bb6a;cursor:pointer;text-decoration:none}a:hover{color:#2e7d32}.background{z-index:-1;background-color:#e6e6e6;width:100%;height:100%;position:fixed;top:0;left:0}nav{background-color:#616161;justify-content:space-between;align-items:center;width:min-content;display:flex;position:fixed;top:0;right:0}nav a{color:#fafafa;padding:.3rem;transition:color .2s,background-color .2s}nav a:hover{color:#fff;background-color:#212121}.markdown-body{color:#212121;box-sizing:border-box;background-color:#fafafa;width:100%;max-width:950px;min-height:300px;margin:3rem auto;padding:30px;overflow-x:hidden;box-shadow:0 2px 6px #6464644d}.markdown-body img,.markdown-body video,.markdown-body iframe,.markdown-body object,.markdown-body embed,.markdown-body svg,.markdown-body canvas,.markdown-body audio{max-width:100%}.markdown-body a{transition:color .2s,background-color .2s}.markdown-body a:hover{background-color:#ffffff1a}.markdown-body ul,.markdown-body ol{padding-left:2rem}.markdown-body pre{background-color:#0000000d;border-radius:.1rem;padding:.4rem;overflow-y:scroll}.markdown-body code{font-family:cousine nerd font mono,monospace}.markdown-body p code{background-color:#0000000d;border-radius:.1rem;padding:.2rem;font-size:small}.markdown-body blockquote{background-color:#0000000d;border-left:5px solid #0000001a;margin:.5rem 2rem;padding:.4rem;font-size:small}.markdown-body details{border:1px solid #0000001a;border-radius:.1rem}.markdown-body summary{cursor:pointer;background-color:#0000000d;padding:.4rem;font-weight:700}.markdown-body summary code{cursor:text}.markdown-body details a{margin-left:-2rem}.markdown-body table{border-spacing:0;border-collapse:separate;border:.1rem solid #0000000d;border-radius:.1rem;width:100%}.markdown-body table td,.markdown-body table th{border-top:.1rem solid #0000000d;border-right:.1rem solid #0000000d}.markdown-body table td:last-child,.markdown-body th:last-child{border-right-color:#0000}.markdown-body table thead tr:first-child>*{border-top:.1rem solid #0000}.loading-bar{z-index:99999;opacity:0;transition:opacity .4s linear;position:fixed;top:0;left:0;& .progress{background-color:#fff;width:0;height:4px;position:fixed;top:0;left:0;box-shadow:0 0 10px #66bb6ab3}&.loading{opacity:1;transition:none;& .progress{transition:width .4s}}}.translateSelectLanguage{color:inherit;z-index:10;opacity:0;cursor:pointer;background-color:#616161;width:17%;height:100%;position:absolute;top:0;left:50%}.darkmode--activated .markdown-body img,.darkmode--activated .markdown-body svg{isolation:isolate;mix-blend-mode:difference}.darkmode-layer,.darkmode-toggle{z-index:2}@media only screen and (width<=950px){html,body,.markdown-body{height:100%}.markdown-body{min-width:256px;margin:0;top:0;overflow-y:scroll}}</style></head><body><nav><a class=back-btn title=返回> <svg class="icon icon-tabler icons-tabler-outline icon-tabler-arrow-back" viewbox="0 0 24 24" fill=none height=24 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 width=24 xmlns=http://www.w3.org/2000/svg><path d="M0 0h24v24H0z" fill=none stroke=none /><path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1"/></svg> </a><a class=refresh-btn href title=刷新> <svg class="icon icon-tabler icons-tabler-outline icon-tabler-refresh" viewbox="0 0 24 24" fill=none height=24 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 width=24 xmlns=http://www.w3.org/2000/svg><path d="M0 0h24v24H0z" fill=none stroke=none /><path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"/><path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"/></svg> </a><a class=to-home title=主页> <svg class="icon icon-tabler icons-tabler-outline icon-tabler-home" viewbox="0 0 24 24" fill=none height=24 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 width=24 xmlns=http://www.w3.org/2000/svg><path d="M0 0h24v24H0z" fill=none stroke=none /><path d="M5 12l-2 0l9 -9l9 9l-2 0"/><path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"/><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"/></svg> </a><a id=translate title=更改语言> <svg class="icon icon-tabler icons-tabler-outline icon-tabler-language" viewbox="0 0 24 24" fill=none height=24 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 width=24 xmlns=http://www.w3.org/2000/svg><path d="M0 0h24v24H0z" fill=none stroke=none /><path d="M4 5h7"/><path d="M9 3v2c0 4.418 -2.239 8 -5 8"/><path d="M5 9c0 2.144 2.952 3.908 6.7 4"/><path d="M12 20l4 -9l4 9"/><path d="M19.1 18h-6.2"/></svg> </a><a class=dark-btn href=javascript:; title=切换显示模式> <svg class="icon icon-tabler icons-tabler-outline icon-tabler-brightness" viewbox="0 0 24 24" fill=none height=24 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 width=24 xmlns=http://www.w3.org/2000/svg><path d="M0 0h24v24H0z" fill=none stroke=none /><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"/><path d="M12 3l0 18"/><path d="M12 9l4.65 -4.65"/><path d="M12 14.3l7.37 -7.37"/><path d="M12 19.6l8.85 -8.85"/></svg> </a><a href=https://github.com/PJ-568/markdown.html/ title=关于> <svg class="icon icon-tabler icons-tabler-outline icon-tabler-info-circle" viewbox="0 0 24 24" fill=none height=24 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 width=24 xmlns=http://www.w3.org/2000/svg><path d="M0 0h24v24H0z" fill=none stroke=none /><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"/><path d="M12 9h.01"/><path d="M11 12h1v4h1"/></svg> </a></nav><div class=markdown-body><h1>加载中……</h1><p>请稍等。</p></div><div class=loading-bar><div class=progress></div></div><div class=background></div><script src=//lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/instant.page/5.1.0/instantpage.min.js></script></body></html>
24 changes: 24 additions & 0 deletions scripts/python/gen_min_html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from htmlmin import minify
import sys

def conpress(file_path, target_path):
# 读取 HTML 文件
with open(file_path, 'r', encoding='utf-8') as f:
# 写入压缩后的 HTML 文件
with open(target_path, 'w', encoding='utf-8') as t:
t.write(minify(f.read(), remove_comments=True, remove_empty_space=True))

if __name__ == "__main__":
"""
压缩指定的 HTML 文件。
:param path/to/file: 欲读取的源文件路径
:param path/to/file: 欲写入的目标文件路径
"""
if len(sys.argv) != 3:
print("使用方法:python gen_min_html.py <源文件路径> <目标文件路径>")
sys.exit(1)

file_path = sys.argv[1]
target_path = sys.argv[2]
conpress(file_path, target_path)
1 change: 1 addition & 0 deletions scripts/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
htmlmin==0.1.12
44 changes: 44 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use minify_html::{minify, Cfg};
use std::fs::write;

fn main() {
if std::env::args().len() < 3 {
eprintln!("Usage: {} <path_to_html_file> <output_path>", std::env::args().next().unwrap());
return;
}

let input_path = std::env::args().nth(1).expect("Expected a path to an HTML file");
let output_path = std::env::args().nth(2).expect("Expected an output file path");

let code = match std::fs::read(input_path) {
Ok(data) => data,
Err(e) => {
eprintln!("Error reading file: {}", e);
return;
}
};

let mut cfg = Cfg::new();
cfg.do_not_minify_doctype = true;
cfg.ensure_spec_compliant_unquoted_attribute_values = true;
cfg.keep_closing_tags = true;
cfg.keep_html_and_head_opening_tags = true;
cfg.keep_spaces_between_attributes = true;
cfg.keep_comments = false;
cfg.keep_input_type_text_attr = true;
cfg.keep_ssi_comments = false;
// cfg.preserve_brace_template_syntax = false;
// cfg.preserve_chevron_percent_template_syntax = false;
cfg.minify_css = true;
cfg.minify_js = true;
cfg.remove_bangs = false;
cfg.remove_processing_instructions = false;

let minified = minify(&code, &cfg);

if let Err(e) = write(&output_path, minified) {
eprintln!("Error writing to file: {}", e);
} else {
println!("Minified HTML written to {}", output_path);
}
}

0 comments on commit 1587638

Please sign in to comment.