astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
126 lines (125 loc) • 4.8 kB
JavaScript
import * as unifont from "unifont";
import { LOCAL_PROVIDER_NAME } from "./constants.js";
import { extractUnifontProviders } from "./logic/extract-unifont-providers.js";
import { normalizeRemoteFontFaces } from "./logic/normalize-remote-font-faces.js";
import { optimizeFallbacks } from "./logic/optimize-fallbacks.js";
import { resolveFamilies } from "./logic/resolve-families.js";
import { resolveLocalFont } from "./providers/local.js";
import { pickFontFaceProperty, unifontFontFaceDataToProperties } from "./utils.js";
async function orchestrate({
families,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver,
storage,
cssRenderer,
systemFallbacksProvider,
fontMetricsResolver,
fontTypeExtractor,
createUrlProxy,
defaults
}) {
let resolvedFamilies = await resolveFamilies({
families,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver
});
const extractedUnifontProvidersResult = extractUnifontProviders({
families: resolvedFamilies,
hasher
});
resolvedFamilies = extractedUnifontProvidersResult.families;
const unifontProviders = extractedUnifontProvidersResult.providers;
const { resolveFont } = await unifont.createUnifont(unifontProviders, {
storage
});
const fontFileDataMap = /* @__PURE__ */ new Map();
const consumableMap = /* @__PURE__ */ new Map();
for (const family of resolvedFamilies) {
const preloadData = [];
let css = "";
const collectedFonts = [];
const fallbacks = family.fallbacks ?? defaults.fallbacks ?? [];
const urlProxy = createUrlProxy({
local: family.provider === LOCAL_PROVIDER_NAME,
hasUrl: (hash) => fontFileDataMap.has(hash),
saveUrl: ({ hash, url, init }) => {
fontFileDataMap.set(hash, { url, init });
},
savePreload: (preload) => {
preloadData.push(preload);
},
saveFontData: (collected) => {
if (fallbacks && fallbacks.length > 0 && // If the same data has already been sent for this family, we don't want to have
// duplicated fallbacks. Such scenario can occur with unicode ranges.
!collectedFonts.some((f) => JSON.stringify(f.data) === JSON.stringify(collected.data))) {
collectedFonts.push(collected);
}
}
});
let fonts;
if (family.provider === LOCAL_PROVIDER_NAME) {
const result = resolveLocalFont({
family,
urlProxy,
fontTypeExtractor
});
fonts = result.fonts;
} else {
const result = await resolveFont(
family.name,
// We do not merge the defaults, we only provide defaults as a fallback
{
weights: family.weights ?? defaults.weights,
styles: family.styles ?? defaults.styles,
subsets: family.subsets ?? defaults.subsets,
fallbacks: family.fallbacks ?? defaults.fallbacks
},
// By default, unifont goes through all providers. We use a different approach where
// we specify a provider per font. Name has been set while extracting unifont providers
// from families (inside extractUnifontProviders).
[family.provider.name]
);
fonts = normalizeRemoteFontFaces({ fonts: result.fonts, urlProxy });
}
for (const data of fonts) {
css += cssRenderer.generateFontFace(
family.nameWithHash,
unifontFontFaceDataToProperties({
src: data.src,
weight: data.weight,
style: data.style,
// User settings override the generated font settings. We use a helper function
// because local and remote providers store this data in different places.
display: pickFontFaceProperty("display", { data, family }),
unicodeRange: pickFontFaceProperty("unicodeRange", { data, family }),
stretch: pickFontFaceProperty("stretch", { data, family }),
featureSettings: pickFontFaceProperty("featureSettings", { data, family }),
variationSettings: pickFontFaceProperty("variationSettings", { data, family })
})
);
}
const cssVarValues = [family.nameWithHash];
const optimizeFallbacksResult = await optimizeFallbacks({
family,
fallbacks,
collectedFonts,
enabled: family.optimizedFallbacks ?? defaults.optimizedFallbacks ?? false,
systemFallbacksProvider,
fontMetricsResolver
});
if (optimizeFallbacksResult) {
css += optimizeFallbacksResult.css;
cssVarValues.push(...optimizeFallbacksResult.fallbacks);
} else {
cssVarValues.push(...fallbacks);
}
css += cssRenderer.generateCssVariable(family.cssVariable, cssVarValues);
consumableMap.set(family.cssVariable, { preloadData, css });
}
return { fontFileDataMap, consumableMap };
}
export {
orchestrate
};