feat: add safe project upgrades and production performance fixes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.2.13",
|
||||
"version": "0.2.14",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -175,6 +175,36 @@ export function renderFontHead(fonts?: FontConfig): string {
|
||||
return out.join("\n ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Production variant that inlines the small Google Fonts stylesheet at build
|
||||
* time. This removes a render-blocking CSS round trip while retaining the same
|
||||
* font files, `font-display`, CSP sources, and offline-safe fallback markup.
|
||||
*/
|
||||
export async function renderProductionFontHead(
|
||||
fonts?: FontConfig,
|
||||
fetcher: (input: string, init?: RequestInit) => Promise<Response> = fetch,
|
||||
): Promise<string> {
|
||||
const fallback = renderFontHead(fonts);
|
||||
if (!fonts?.google?.length) return fallback;
|
||||
const url = googleFontsUrl(fonts.google, fonts.display ?? "swap");
|
||||
try {
|
||||
const response = await fetcher(url, {
|
||||
headers: {
|
||||
// Google returns compact WOFF2 rules to modern browser user agents.
|
||||
"user-agent": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36",
|
||||
},
|
||||
});
|
||||
if (!response.ok) return fallback;
|
||||
const css = (await response.text()).replace(/<\/style/gi, "<\\/style");
|
||||
const stylesheet = `<link rel="stylesheet" href="${escAttr(url)}">`;
|
||||
return fallback
|
||||
.replace(`<link rel="preconnect" href="${GOOGLE_CSS}">\n `, "")
|
||||
.replace(stylesheet, `<style data-wrnexus-google-fonts>\n${css}\n</style>`);
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CSP source hosts required by the configured fonts, so the policy can be
|
||||
* auto-extended (Google Fonts need their CSS + static hosts allow-listed).
|
||||
|
||||
@@ -18,7 +18,7 @@ export type {
|
||||
export { loadAppConfig, loadRawConfig, headToString, resolveProfile, loadEnv } from "./config.ts";
|
||||
export { findStyleEntry, bundleCss } from "./styles.ts";
|
||||
export type { FontConfig, GoogleFont, LocalFontFace, FontDisplay } from "./fonts.ts";
|
||||
export { renderFontHead, fontCspSources } from "./fonts.ts";
|
||||
export { renderFontHead, renderProductionFontHead, fontCspSources } from "./fonts.ts";
|
||||
export type { ThemeConfig, ThemeTokens, ResolvedTheme } from "./theme.ts";
|
||||
export {
|
||||
DEFAULT_THEMES,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { fontCspSources, renderFontHead } from "../src/index.ts";
|
||||
import { fontCspSources, renderFontHead, renderProductionFontHead } from "../src/index.ts";
|
||||
|
||||
test("renders subsetted Google and escaped local font markup", () => {
|
||||
const html = renderFontHead({
|
||||
@@ -20,3 +20,13 @@ test("renders subsetted Google and escaped local font markup", () => {
|
||||
font: ["https://fonts.gstatic.com"],
|
||||
});
|
||||
});
|
||||
|
||||
test("production font head inlines Google CSS and removes its blocking stylesheet", async () => {
|
||||
const html = await renderProductionFontHead(
|
||||
{ google: [{ family: "Plus Jakarta Sans", weights: [400, 700] }] },
|
||||
() => Promise.resolve(new Response('@font-face{font-family:"Plus Jakarta Sans"}')),
|
||||
);
|
||||
expect(html).toContain("data-wrnexus-google-fonts");
|
||||
expect(html).toContain("@font-face");
|
||||
expect(html).not.toContain('<link rel="stylesheet" href="https://fonts.googleapis.com');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user