Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ jobs:
done
shell: bash

- name: Rewrite clean URLs to directory style
# VitePress cleanUrls:true emits foo.html and expects the host to
# rewrite /foo/ -> /foo.html. GitHub Pages doesn't, so every browse
# page 404s on its directory-style URL. Convert each non-index .html
# to <name>/index.html so /foo/ resolves natively.
run: |
find dist -type f -name "*.html" ! -name "index.html" | while read -r f; do
dir="$(dirname "$f")/$(basename "$f" .html)"
mkdir -p "$dir"
mv "$f" "$dir/index.html"
done
echo "Rewrote $(find dist -type d -name index.html -exec dirname {} \; | wc -l) pages"
shell: bash

- name: Upload combined artifact
uses: actions/upload-pages-artifact@v4
with:
Expand Down
29 changes: 29 additions & 0 deletions docs/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,31 @@ async function countHtmlFiles(dir) {
return n;
}

// VitePress with cleanUrls:true emits foo.html and expects the host to
// rewrite /foo/ -> /foo.html. GitHub Pages doesn't, so every browse page
// 404s on its directory-style URL. Convert each non-index .html to
// <name>/index.html so /foo/ resolves natively.
async function rewriteCleanUrls(dir) {
let count = 0;
async function walk(d) {
const entries = await readdir(d, { withFileTypes: true });
for (const e of entries) {
const full = join(d, e.name);
if (e.isDirectory()) {
await walk(full);
} else if (e.isFile() && e.name.endsWith(".html") && e.name !== "index.html") {
const baseName = e.name.slice(0, -5);
const newDir = join(d, baseName);
await mkdir(newDir, { recursive: true });
await rename(full, join(newDir, "index.html"));
count++;
}
}
}
await walk(dir);
return count;
}

function run(cmd, args, env = process.env) {
const result = spawnSync(cmd, args, {
stdio: "inherit",
Expand Down Expand Up @@ -157,6 +182,10 @@ async function main() {
await rm(DIST_DIR, { recursive: true, force: true });
await rename(FINAL_DIR, DIST_DIR);

console.log("=== Rewriting clean URLs to directory style ===");
const rewritten = await rewriteCleanUrls(DIST_DIR);
console.log(`Rewrote ${rewritten} pages`);

console.log(`\n=== Build complete: ${await countHtmlFiles(DIST_DIR)} total HTML pages ===`);
}

Expand Down
88 changes: 62 additions & 26 deletions docs/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ function escapeYAMLString(str) {
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
}

// Prevent markdown autolinking of bare URLs in body text (descriptions,
// copyrights). The backslash escapes the colon so markdown-it renders the
// URL as plain text instead of an <a href>. License/EULA body text is
// already safe — it ships inside ```code blocks```.
function escapeBareUrls(str) {
if (!str) return "";
return str.replace(/(https?:)(\/\/)/g, "$1\\$2");
}

// Detect formula source type from path
function detectSourceType(slug) {
if (slug.startsWith("google/")) return "google";
Expand Down Expand Up @@ -85,16 +94,37 @@ function detectLicenseInfo(yaml, sourceType) {
// Combine all text for license detection
const allText = `${licenseUrl} ${openLicense} ${copyright}`.toLowerCase();

// Platform-tied sources (macOS) win over SPDX detection: a macOS font is
// platform_restricted regardless of any SPDX code on the formula, because
// the SPDX code (often LicenseRef-Apple-EA1705) describes the EULA text,
// not the redistribution category. Without this guard, all 2,107 macOS
// fonts get miscategorized as open_source via the SPDX fallback below.
if (sourceType === "macos") {
return {
type: "macos",
name: "Apple-only License",
badge: svgBadge("License", "Apple-only", "#f0ad4e", 120),
docLink: "/licenses/apple-only",
spdxUrl: null,
category: "platform_restricted",
isOpen: false,
warning: "⚠️ **Platform Restricted**: These fonts are licensed for use on macOS only. Installation on other platforms may violate Apple's license terms.",
};
}

// Fast path: use spdx_license field if available
if (spdxLicense) {
if (spdxLicense.startsWith("OFL-1.1")) {
const isRfn = spdxLicense.includes("-RFN");
// SPDX URLs are case-sensitive: OFL-1.1-RFN.html and OFL-1.1-no-RFN.html
// exist, but OFL-1.1-NO-RFN.html does not. YAML stores it uppercase.
const spdxPath = spdxLicense.replace("-NO-RFN", "-no-RFN");
return {
type: "ofl",
name: `SIL Open Font License 1.1${isRfn ? " (with RFN)" : ""}`,
badge: svgBadge("License", isRfn ? "OFL 1.1-RFN" : "OFL 1.1", "#28a745", isRfn ? 140 : 120),
docLink: "/licenses/ofl",
spdxUrl: `https://spdx.org/licenses/${spdxLicense}.html`,
spdxUrl: `https://spdx.org/licenses/${spdxPath}.html`,
category: "open_source",
isOpen: true,
};
Expand Down Expand Up @@ -171,30 +201,36 @@ function detectLicenseInfo(yaml, sourceType) {
category: "open_source", isOpen: true,
};
}
// Generic fallback for known SPDX codes not matched above
return {
type: "spdx_other", name: spdxLicense,
badge: svgBadge("License", spdxLicense.slice(0, 15), "#28a745", 120),
docLink: null, spdxUrl: `https://spdx.org/licenses/${spdxLicense}.html`,
category: "open_source", isOpen: true,
};
}

// ============================================================
// PLATFORM RESTRICTED
// ============================================================

// macOS fonts
if (sourceType === "macos") {
// Generic fallback for SPDX codes not matched above.
// LicenseRef-* codes are vendor-specific references with no SPDX page
// (spdxUrl=null). Categorize by vendor prefix so the browse page and
// stats reflect the right redistribution category instead of dumping
// everything into open_source.
const isLicenseRef = spdxLicense.startsWith("LICENSEREF-");
let refType = "spdx_other";
let refCategory = "open_source";
let refBadgeText = spdxLicense.slice(0, 15);
let refBadgeColor = "#28a745";
if (isLicenseRef) {
if (spdxLicense.startsWith("LICENSEREF-APPLE-")) {
refType = "macos"; refCategory = "platform_restricted";
refBadgeText = "Apple-only"; refBadgeColor = "#f0ad4e";
} else if (spdxLicense.startsWith("LICENSEREF-MICROSOFT-FONTPACK-")) {
refType = "ms_web_fonts"; refCategory = "freely_distributable";
refBadgeText = "MS Web Fonts"; refBadgeColor = "#007bff";
} else if (spdxLicense.startsWith("LICENSEREF-MICROSOFT-")) {
refType = "ms_office"; refCategory = "bundled_software";
refBadgeText = "MS Software"; refBadgeColor = "#8b5cf6";
} else if (spdxLicense.startsWith("LICENSEREF-ADOBE-")) {
refType = "adobe"; refCategory = "bundled_software";
refBadgeText = "Adobe Software"; refBadgeColor = "#8b5cf6";
}
}
return {
type: "macos",
name: "Apple-only License",
badge: svgBadge("License", "Apple-only", "#f0ad4e", 120),
docLink: "/licenses/apple-only",
spdxUrl: null,
category: "platform_restricted",
isOpen: false,
warning: "⚠️ **Platform Restricted**: These fonts are licensed for use on macOS only. Installation on other platforms may violate Apple's license terms.",
type: refType, name: spdxLicense,
badge: svgBadge("License", refBadgeText, refBadgeColor, 120),
docLink: null, spdxUrl: isLicenseRef ? null : `https://spdx.org/licenses/${spdxLicense}.html`,
category: refCategory, isOpen: refCategory === "open_source",
};
}

Expand Down Expand Up @@ -899,7 +935,7 @@ ${licenseInfo.warning || ""}${licenseTextSection}${
copyrightSection = `## Copyright

${[...allCopyrights]
.map((c) => `- ${c.replace(/</g, "&lt;").replace(/>/g, "&gt;")}`)
.map((c) => `- ${escapeBareUrls(c.replace(/</g, "&lt;").replace(/>/g, "&gt;"))}`)
.join("\n")}
`;
}
Expand Down Expand Up @@ -950,7 +986,7 @@ outline: [2, 3]

${badges}

${yaml.description && yaml.description !== displayName ? yaml.description + "\n" : ""}## Quick Install
${yaml.description && yaml.description !== displayName ? escapeBareUrls(yaml.description) + "\n" : ""}## Quick Install

\`\`\`bash
${installCmd}
Expand Down
5 changes: 4 additions & 1 deletion docs/lychee.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ exclude_all_private = false
include_mail = false

############################# Other #############################
include_verbatim = true
# Only check URLs in <a href>/<img src>/markdown links — skip URLs that appear
# verbatim in code blocks (license/EULA text) or plain text (descriptions).
# Those are informational mentions, not links we maintain.
include_verbatim = false
index_files = ["index.html"]
Loading