Files
homewebsite/node_modules/astro/dist/assets/fonts/orchestrate.js
2025-10-17 20:17:33 +00:00

161 lines
5.9 KiB
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,
renderFontWeight,
unifontFontFaceDataToProperties
} from "./utils.js";
async function orchestrate({
families,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver,
storage,
cssRenderer,
systemFallbacksProvider,
fontMetricsResolver,
fontTypeExtractor,
fontFileReader,
logger,
createUrlProxy,
defaults,
bold,
stringMatcher
}) {
let resolvedFamilies = await resolveFamilies({
families,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver
});
const extractedUnifontProvidersResult = extractUnifontProviders({
families: resolvedFamilies,
hasher
});
resolvedFamilies = extractedUnifontProvidersResult.families;
const unifontProviders = extractedUnifontProvidersResult.providers;
const { resolveFont, listFonts } = await unifont.createUnifont(unifontProviders, {
storage
});
const fontFileDataMap = /* @__PURE__ */ new Map();
const internalConsumableMap = /* @__PURE__ */ new Map();
const consumableMap = /* @__PURE__ */ new Map();
for (const family of resolvedFamilies) {
const preloadData = [];
const consumableMapValue = [];
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);
}
},
cssVariable: family.cssVariable
});
let fonts;
if (family.provider === LOCAL_PROVIDER_NAME) {
const result = resolveLocalFont({
family,
urlProxy,
fontTypeExtractor,
fontFileReader
});
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]
);
if (result.fonts.length === 0) {
logger.warn(
"assets",
`No data found for font family ${bold(family.name)}. Review your configuration`
);
const availableFamilies = await listFonts([family.provider.name]);
if (availableFamilies && !availableFamilies.includes(family.name)) {
logger.warn(
"assets",
`${bold(family.name)} font family cannot be retrieved by the provider. Did you mean ${bold(stringMatcher.getClosestMatch(family.name, availableFamilies))}?`
);
}
}
fonts = normalizeRemoteFontFaces({ fonts: result.fonts, urlProxy, fontTypeExtractor });
}
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 })
})
);
consumableMapValue.push({
weight: renderFontWeight(data.weight),
style: data.style,
src: data.src.filter((src) => "url" in src).map((src) => ({
url: src.url,
format: src.format,
tech: src.tech
}))
});
}
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);
internalConsumableMap.set(family.cssVariable, { preloadData, css });
consumableMap.set(family.cssVariable, consumableMapValue);
}
return { fontFileDataMap, internalConsumableMap, consumableMap };
}
export {
orchestrate
};