fr-docs 2.1.1__tar.gz → 2.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fr_docs-2.1.1 → fr_docs-2.1.3}/PKG-INFO +1 -1
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/build.py +82 -2
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/template.py +1 -4
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs.egg-info/PKG-INFO +1 -1
- {fr_docs-2.1.1 → fr_docs-2.1.3}/pyproject.toml +1 -1
- {fr_docs-2.1.1 → fr_docs-2.1.3}/LICENSE.md +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/README.md +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/__init__.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/__main__.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/config.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/config_accessors.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/frontmatter.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/git.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/html_pipeline.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/init.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/markdown.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/search.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/slug.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/syntax.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs/utils.py +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs.egg-info/SOURCES.txt +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs.egg-info/dependency_links.txt +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs.egg-info/entry_points.txt +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs.egg-info/requires.txt +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/fr_docs.egg-info/top_level.txt +0 -0
- {fr_docs-2.1.1 → fr_docs-2.1.3}/setup.cfg +0 -0
|
@@ -8,8 +8,11 @@ import argparse
|
|
|
8
8
|
import concurrent.futures
|
|
9
9
|
import json
|
|
10
10
|
import os
|
|
11
|
+
import re
|
|
11
12
|
import shutil
|
|
13
|
+
import subprocess
|
|
12
14
|
import sys
|
|
15
|
+
import tempfile
|
|
13
16
|
from pathlib import Path
|
|
14
17
|
|
|
15
18
|
import xxhash
|
|
@@ -42,6 +45,80 @@ def _compute_hash(data: str) -> str:
|
|
|
42
45
|
return xxhash.xxh3_128_hexdigest(data.encode("utf-8"))
|
|
43
46
|
|
|
44
47
|
|
|
48
|
+
def _minify_static_asset(config, src: Path, dst: Path, name: str) -> None:
|
|
49
|
+
"""Minify a static asset in-place (JS via terser, CSS via html-minifier).
|
|
50
|
+
|
|
51
|
+
Falls back to a plain copy if the minifier is unavailable. CSS is minified
|
|
52
|
+
by wrapping it in a `<style>` tag (html-minifier-next only processes CSS
|
|
53
|
+
embedded in HTML) and stripping the wrapper afterwards.
|
|
54
|
+
"""
|
|
55
|
+
repo_root = Path(config["_docs_dir"]).resolve().parent
|
|
56
|
+
node_modules = repo_root / "node_modules"
|
|
57
|
+
|
|
58
|
+
if name.endswith(".js"):
|
|
59
|
+
terser_bin = node_modules / ".bin" / "terser"
|
|
60
|
+
cmd = [
|
|
61
|
+
str(terser_bin) if terser_bin.exists() else "npx",
|
|
62
|
+
"--yes" if not terser_bin.exists() else "",
|
|
63
|
+
*(["terser"] if not terser_bin.exists() else []),
|
|
64
|
+
str(src),
|
|
65
|
+
"--compress",
|
|
66
|
+
"warnings=false",
|
|
67
|
+
"--mangle",
|
|
68
|
+
"--output",
|
|
69
|
+
str(dst),
|
|
70
|
+
]
|
|
71
|
+
cmd = [c for c in cmd if c]
|
|
72
|
+
result = subprocess.run(
|
|
73
|
+
cmd, capture_output=True, text=True, check=False, cwd=str(repo_root)
|
|
74
|
+
)
|
|
75
|
+
if result.returncode != 0 or not dst.exists() or dst.stat().st_size == 0:
|
|
76
|
+
print(f" ! minify failed for {name}, copying raw: {result.stderr[-300:]}")
|
|
77
|
+
shutil.copyfile(src, dst)
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# CSS: html-minifier-next only minifies CSS embedded in HTML, so wrap the
|
|
81
|
+
# stylesheet in a <style> tag, minify, then strip the wrapper.
|
|
82
|
+
minifier_bin = node_modules / ".bin" / "html-minifier-next"
|
|
83
|
+
raw_css = src.read_text(encoding="utf-8")
|
|
84
|
+
wrapped = f"<style>{raw_css}</style>"
|
|
85
|
+
wrapper = Path(tempfile.mkstemp(suffix=".html")[1])
|
|
86
|
+
min_out = Path(tempfile.mkstemp(suffix=".html")[1])
|
|
87
|
+
try:
|
|
88
|
+
wrapper.write_text(wrapped, encoding="utf-8")
|
|
89
|
+
cmd = [
|
|
90
|
+
str(minifier_bin) if minifier_bin.exists() else "npx",
|
|
91
|
+
"--yes" if not minifier_bin.exists() else "",
|
|
92
|
+
*(["html-minifier-next"] if not minifier_bin.exists() else ""),
|
|
93
|
+
"--minify-css=true",
|
|
94
|
+
"--remove-comments",
|
|
95
|
+
"--output",
|
|
96
|
+
str(min_out),
|
|
97
|
+
str(wrapper),
|
|
98
|
+
]
|
|
99
|
+
cmd = [c for c in cmd if c]
|
|
100
|
+
result = subprocess.run(
|
|
101
|
+
cmd, capture_output=True, text=True, check=False, cwd=str(repo_root)
|
|
102
|
+
)
|
|
103
|
+
if result.returncode == 0 and min_out.exists():
|
|
104
|
+
out = min_out.read_text(encoding="utf-8")
|
|
105
|
+
m = re.search(r"<style>([\s\S]*)</style>", out)
|
|
106
|
+
if m:
|
|
107
|
+
dst.write_text(m.group(1), encoding="utf-8")
|
|
108
|
+
return
|
|
109
|
+
print(f" ! minify failed for {name}, copying raw: {result.stderr[-300:]}")
|
|
110
|
+
finally:
|
|
111
|
+
try:
|
|
112
|
+
wrapper.unlink()
|
|
113
|
+
except OSError:
|
|
114
|
+
pass
|
|
115
|
+
try:
|
|
116
|
+
min_out.unlink()
|
|
117
|
+
except OSError:
|
|
118
|
+
pass
|
|
119
|
+
shutil.copyfile(src, dst)
|
|
120
|
+
|
|
121
|
+
|
|
45
122
|
class BuildCache:
|
|
46
123
|
"""Manage caching of build data to avoid unnecessary regeneration."""
|
|
47
124
|
|
|
@@ -287,7 +364,7 @@ def main(argv=None):
|
|
|
287
364
|
config["_source_files"] = source_files
|
|
288
365
|
print(f" Found {len(source_files)} source files")
|
|
289
366
|
|
|
290
|
-
# Copy static assets
|
|
367
|
+
# Copy static assets (minifying JS/CSS when production)
|
|
291
368
|
docs_path = Path(config["_docs_dir"])
|
|
292
369
|
os.makedirs(Path(config["_out_dir"]), exist_ok=True)
|
|
293
370
|
for legacy_name in (
|
|
@@ -303,7 +380,10 @@ def main(argv=None):
|
|
|
303
380
|
dst = Path(config["_out_dir"]) / name
|
|
304
381
|
|
|
305
382
|
try:
|
|
306
|
-
|
|
383
|
+
if config.get("production", False) and name.endswith((".js", ".css")):
|
|
384
|
+
_minify_static_asset(config, src, dst, name)
|
|
385
|
+
else:
|
|
386
|
+
shutil.copyfile(src, dst)
|
|
307
387
|
except FileNotFoundError:
|
|
308
388
|
print(f"File not found: {os.getcwd()}, {src}->{dst}")
|
|
309
389
|
except OSError as e:
|
|
@@ -21,10 +21,7 @@ TEMPLATE = """\
|
|
|
21
21
|
<link rel="icon" href="{site_prefix}favicon.svg" type="image/svg+xml">
|
|
22
22
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
23
23
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
24
|
-
|
|
25
|
-
<link rel="preload" as="font" href="https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmM.woff2" type="font/woff2" crossorigin>
|
|
26
|
-
<link rel="preload" as="font" href="https://fonts.gstatic.com/s/inter/v20/UcCo3FwrK3iLTcviYwYZ8UA3.woff2" type="font/woff2" crossorigin>
|
|
27
|
-
<link rel="preload" as="font" href="https://fonts.gstatic.com/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0P.woff2" type="font/woff2" crossorigin>
|
|
24
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,400..800&family=JetBrains+Mono:wght@400..700&display=swap" rel="stylesheet">
|
|
28
25
|
</head>
|
|
29
26
|
<body>
|
|
30
27
|
<!-- Header -->
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|