astro 5.7.6 → 5.7.8
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.
- package/dist/assets/fonts/config.d.ts +3 -3
- package/dist/assets/fonts/config.js +2 -3
- package/dist/assets/fonts/constants.d.ts +3 -73
- package/dist/assets/fonts/constants.js +16 -68
- package/dist/assets/fonts/definitions.d.ts +73 -0
- package/dist/assets/fonts/definitions.js +0 -0
- package/dist/assets/fonts/implementations/css-renderer.d.ts +9 -0
- package/dist/assets/fonts/implementations/css-renderer.js +42 -0
- package/dist/assets/fonts/implementations/data-collector.d.ts +3 -0
- package/dist/assets/fonts/implementations/data-collector.js +21 -0
- package/dist/assets/fonts/implementations/error-handler.d.ts +2 -0
- package/dist/assets/fonts/implementations/error-handler.js +33 -0
- package/dist/assets/fonts/implementations/font-fetcher.d.ts +8 -0
- package/dist/assets/fonts/implementations/font-fetcher.js +34 -0
- package/dist/assets/fonts/implementations/font-metrics-resolver.d.ts +5 -0
- package/dist/assets/fonts/implementations/font-metrics-resolver.js +60 -0
- package/dist/assets/fonts/implementations/font-type-extractor.d.ts +4 -0
- package/dist/assets/fonts/implementations/font-type-extractor.js +22 -0
- package/dist/assets/fonts/implementations/hasher.d.ts +2 -0
- package/dist/assets/fonts/implementations/hasher.js +14 -0
- package/dist/assets/fonts/implementations/local-provider-url-resolver.d.ts +5 -0
- package/dist/assets/fonts/implementations/local-provider-url-resolver.js +17 -0
- package/dist/assets/fonts/implementations/remote-font-provider-mod-resolver.d.ts +6 -0
- package/dist/assets/fonts/implementations/remote-font-provider-mod-resolver.js +20 -0
- package/dist/assets/fonts/implementations/remote-font-provider-resolver.d.ts +6 -0
- package/dist/assets/fonts/implementations/remote-font-provider-resolver.js +47 -0
- package/dist/assets/fonts/implementations/storage.d.ts +4 -0
- package/dist/assets/fonts/implementations/storage.js +14 -0
- package/dist/assets/fonts/implementations/system-fallbacks-provider.d.ts +11 -0
- package/dist/assets/fonts/implementations/system-fallbacks-provider.js +74 -0
- package/dist/assets/fonts/implementations/url-proxy-content-resolver.d.ts +5 -0
- package/dist/assets/fonts/implementations/url-proxy-content-resolver.js +28 -0
- package/dist/assets/fonts/implementations/url-proxy.d.ts +8 -0
- package/dist/assets/fonts/implementations/url-proxy.js +26 -0
- package/dist/assets/fonts/logic/extract-unifont-providers.d.ts +10 -0
- package/dist/assets/fonts/logic/extract-unifont-providers.js +28 -0
- package/dist/assets/fonts/logic/normalize-remote-font-faces.d.ts +6 -0
- package/dist/assets/fonts/logic/normalize-remote-font-faces.js +36 -0
- package/dist/assets/fonts/logic/optimize-fallbacks.d.ts +17 -0
- package/dist/assets/fonts/logic/optimize-fallbacks.js +47 -0
- package/dist/assets/fonts/logic/resolve-families.d.ts +17 -0
- package/dist/assets/fonts/logic/resolve-families.js +67 -0
- package/dist/assets/fonts/orchestrate.d.ts +37 -0
- package/dist/assets/fonts/orchestrate.js +125 -0
- package/dist/assets/fonts/providers/local.d.ts +6 -7
- package/dist/assets/fonts/providers/local.js +26 -29
- package/dist/assets/fonts/types.d.ts +32 -2
- package/dist/assets/fonts/utils.d.ts +17 -88
- package/dist/assets/fonts/utils.js +20 -186
- package/dist/assets/fonts/vite-plugin-fonts.js +96 -94
- package/dist/content/content-layer.js +3 -3
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/types/public/config.d.ts +2 -2
- package/dist/vite-plugin-markdown/images.js +4 -4
- package/package.json +2 -2
- package/types/content.d.ts +11 -4
- package/dist/assets/fonts/load.d.ts +0 -20
- package/dist/assets/fonts/load.js +0 -162
- package/dist/assets/fonts/metrics.d.ts +0 -10
- package/dist/assets/fonts/metrics.js +0 -55
- package/dist/assets/fonts/providers/utils.d.ts +0 -9
- package/dist/assets/fonts/providers/utils.js +0 -37
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { resolveEntrypoint } from "../utils.js";
|
|
2
|
+
function validateMod({
|
|
3
|
+
mod,
|
|
4
|
+
entrypoint,
|
|
5
|
+
errorHandler
|
|
6
|
+
}) {
|
|
7
|
+
try {
|
|
8
|
+
if (typeof mod !== "object" || mod === null) {
|
|
9
|
+
throw new Error(`Expected an object for the module, but received ${typeof mod}.`);
|
|
10
|
+
}
|
|
11
|
+
if (typeof mod.provider !== "function") {
|
|
12
|
+
throw new Error(`Invalid provider export in module, expected a function.`);
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
provider: mod.provider
|
|
16
|
+
};
|
|
17
|
+
} catch (cause) {
|
|
18
|
+
throw errorHandler.handle({
|
|
19
|
+
type: "cannot-load-font-provider",
|
|
20
|
+
data: {
|
|
21
|
+
entrypoint
|
|
22
|
+
},
|
|
23
|
+
cause
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function createRemoteFontProviderResolver({
|
|
28
|
+
root,
|
|
29
|
+
modResolver,
|
|
30
|
+
errorHandler
|
|
31
|
+
}) {
|
|
32
|
+
return {
|
|
33
|
+
async resolve({ entrypoint, config }) {
|
|
34
|
+
const id = resolveEntrypoint(root, entrypoint.toString()).href;
|
|
35
|
+
const mod = await modResolver.resolve(id);
|
|
36
|
+
const { provider } = validateMod({
|
|
37
|
+
mod,
|
|
38
|
+
entrypoint: id,
|
|
39
|
+
errorHandler
|
|
40
|
+
});
|
|
41
|
+
return { config, provider };
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
createRemoteFontProviderResolver
|
|
47
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { createStorage } from "unstorage";
|
|
3
|
+
import fsLiteDriver from "unstorage/drivers/fs-lite";
|
|
4
|
+
function createFsStorage({ base }) {
|
|
5
|
+
return createStorage({
|
|
6
|
+
// Types are weirly exported
|
|
7
|
+
driver: fsLiteDriver({
|
|
8
|
+
base: fileURLToPath(base)
|
|
9
|
+
})
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
createFsStorage
|
|
14
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SystemFallbacksProvider } from '../definitions.js';
|
|
2
|
+
export declare const DEFAULT_FALLBACKS: {
|
|
3
|
+
serif: "Times New Roman"[];
|
|
4
|
+
'sans-serif': "Arial"[];
|
|
5
|
+
monospace: "Courier New"[];
|
|
6
|
+
'system-ui': ("Arial" | "BlinkMacSystemFont" | "Segoe UI" | "Roboto" | "Helvetica Neue")[];
|
|
7
|
+
'ui-serif': "Times New Roman"[];
|
|
8
|
+
'ui-sans-serif': "Arial"[];
|
|
9
|
+
'ui-monospace': "Courier New"[];
|
|
10
|
+
};
|
|
11
|
+
export declare function createSystemFallbacksProvider(): SystemFallbacksProvider;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const SYSTEM_METRICS = {
|
|
2
|
+
"Times New Roman": {
|
|
3
|
+
ascent: 1825,
|
|
4
|
+
descent: -443,
|
|
5
|
+
lineGap: 87,
|
|
6
|
+
unitsPerEm: 2048,
|
|
7
|
+
xWidthAvg: 832
|
|
8
|
+
},
|
|
9
|
+
Arial: {
|
|
10
|
+
ascent: 1854,
|
|
11
|
+
descent: -434,
|
|
12
|
+
lineGap: 67,
|
|
13
|
+
unitsPerEm: 2048,
|
|
14
|
+
xWidthAvg: 913
|
|
15
|
+
},
|
|
16
|
+
"Courier New": {
|
|
17
|
+
ascent: 1705,
|
|
18
|
+
descent: -615,
|
|
19
|
+
lineGap: 0,
|
|
20
|
+
unitsPerEm: 2048,
|
|
21
|
+
xWidthAvg: 1229
|
|
22
|
+
},
|
|
23
|
+
BlinkMacSystemFont: {
|
|
24
|
+
ascent: 1980,
|
|
25
|
+
descent: -432,
|
|
26
|
+
lineGap: 0,
|
|
27
|
+
unitsPerEm: 2048,
|
|
28
|
+
xWidthAvg: 853
|
|
29
|
+
},
|
|
30
|
+
"Segoe UI": {
|
|
31
|
+
ascent: 2210,
|
|
32
|
+
descent: -514,
|
|
33
|
+
lineGap: 0,
|
|
34
|
+
unitsPerEm: 2048,
|
|
35
|
+
xWidthAvg: 908
|
|
36
|
+
},
|
|
37
|
+
Roboto: {
|
|
38
|
+
ascent: 1900,
|
|
39
|
+
descent: -500,
|
|
40
|
+
lineGap: 0,
|
|
41
|
+
unitsPerEm: 2048,
|
|
42
|
+
xWidthAvg: 911
|
|
43
|
+
},
|
|
44
|
+
"Helvetica Neue": {
|
|
45
|
+
ascent: 952,
|
|
46
|
+
descent: -213,
|
|
47
|
+
lineGap: 28,
|
|
48
|
+
unitsPerEm: 1e3,
|
|
49
|
+
xWidthAvg: 450
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const DEFAULT_FALLBACKS = {
|
|
53
|
+
serif: ["Times New Roman"],
|
|
54
|
+
"sans-serif": ["Arial"],
|
|
55
|
+
monospace: ["Courier New"],
|
|
56
|
+
"system-ui": ["BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial"],
|
|
57
|
+
"ui-serif": ["Times New Roman"],
|
|
58
|
+
"ui-sans-serif": ["Arial"],
|
|
59
|
+
"ui-monospace": ["Courier New"]
|
|
60
|
+
};
|
|
61
|
+
function createSystemFallbacksProvider() {
|
|
62
|
+
return {
|
|
63
|
+
getLocalFonts(fallback) {
|
|
64
|
+
return DEFAULT_FALLBACKS[fallback] ?? null;
|
|
65
|
+
},
|
|
66
|
+
getMetricsForLocalFont(family) {
|
|
67
|
+
return SYSTEM_METRICS[family];
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
DEFAULT_FALLBACKS,
|
|
73
|
+
createSystemFallbacksProvider
|
|
74
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ErrorHandler, UrlProxyContentResolver } from '../definitions.js';
|
|
2
|
+
export declare function createLocalUrlProxyContentResolver({ errorHandler, }: {
|
|
3
|
+
errorHandler: ErrorHandler;
|
|
4
|
+
}): UrlProxyContentResolver;
|
|
5
|
+
export declare function createRemoteUrlProxyContentResolver(): UrlProxyContentResolver;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
function createLocalUrlProxyContentResolver({
|
|
3
|
+
errorHandler
|
|
4
|
+
}) {
|
|
5
|
+
return {
|
|
6
|
+
resolve(url) {
|
|
7
|
+
try {
|
|
8
|
+
return url + readFileSync(url, "utf-8");
|
|
9
|
+
} catch (cause) {
|
|
10
|
+
throw errorHandler.handle({
|
|
11
|
+
type: "unknown-fs-error",
|
|
12
|
+
data: {},
|
|
13
|
+
cause
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function createRemoteUrlProxyContentResolver() {
|
|
20
|
+
return {
|
|
21
|
+
// Passthrough, the remote provider URL is enough
|
|
22
|
+
resolve: (url) => url
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
createLocalUrlProxyContentResolver,
|
|
27
|
+
createRemoteUrlProxyContentResolver
|
|
28
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DataCollector, FontTypeExtractor, Hasher, UrlProxy, UrlProxyContentResolver } from '../definitions.js';
|
|
2
|
+
export declare function createUrlProxy({ base, contentResolver, hasher, dataCollector, fontTypeExtractor, }: {
|
|
3
|
+
base: string;
|
|
4
|
+
contentResolver: UrlProxyContentResolver;
|
|
5
|
+
hasher: Hasher;
|
|
6
|
+
dataCollector: DataCollector;
|
|
7
|
+
fontTypeExtractor: FontTypeExtractor;
|
|
8
|
+
}): UrlProxy;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function createUrlProxy({
|
|
2
|
+
base,
|
|
3
|
+
contentResolver,
|
|
4
|
+
hasher,
|
|
5
|
+
dataCollector,
|
|
6
|
+
fontTypeExtractor
|
|
7
|
+
}) {
|
|
8
|
+
return {
|
|
9
|
+
proxy({ url: originalUrl, data, collectPreload, init }) {
|
|
10
|
+
const type = fontTypeExtractor.extract(originalUrl);
|
|
11
|
+
const hash = `${hasher.hashString(contentResolver.resolve(originalUrl))}.${type}`;
|
|
12
|
+
const url = base + hash;
|
|
13
|
+
dataCollector.collect({
|
|
14
|
+
url: originalUrl,
|
|
15
|
+
hash,
|
|
16
|
+
preload: collectPreload ? { url, type } : null,
|
|
17
|
+
data,
|
|
18
|
+
init
|
|
19
|
+
});
|
|
20
|
+
return url;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
createUrlProxy
|
|
26
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type * as unifont from 'unifont';
|
|
2
|
+
import type { Hasher } from '../definitions.js';
|
|
3
|
+
import type { ResolvedFontFamily } from '../types.js';
|
|
4
|
+
export declare function extractUnifontProviders({ families, hasher, }: {
|
|
5
|
+
families: Array<ResolvedFontFamily>;
|
|
6
|
+
hasher: Hasher;
|
|
7
|
+
}): {
|
|
8
|
+
families: Array<ResolvedFontFamily>;
|
|
9
|
+
providers: Array<unifont.Provider>;
|
|
10
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { LOCAL_PROVIDER_NAME } from "../constants.js";
|
|
2
|
+
function extractUnifontProviders({
|
|
3
|
+
families,
|
|
4
|
+
hasher
|
|
5
|
+
}) {
|
|
6
|
+
const hashes = /* @__PURE__ */ new Set();
|
|
7
|
+
const providers = [];
|
|
8
|
+
for (const { provider } of families) {
|
|
9
|
+
if (provider === LOCAL_PROVIDER_NAME) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
const unifontProvider = provider.provider(provider.config);
|
|
13
|
+
const hash = hasher.hashObject({
|
|
14
|
+
name: unifontProvider._name,
|
|
15
|
+
...provider.config
|
|
16
|
+
});
|
|
17
|
+
unifontProvider._name += `-${hash}`;
|
|
18
|
+
provider.name = unifontProvider._name;
|
|
19
|
+
if (!hashes.has(hash)) {
|
|
20
|
+
hashes.add(hash);
|
|
21
|
+
providers.push(unifontProvider);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { families, providers };
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
extractUnifontProviders
|
|
28
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function normalizeRemoteFontFaces({
|
|
2
|
+
fonts,
|
|
3
|
+
urlProxy
|
|
4
|
+
}) {
|
|
5
|
+
return fonts.filter((font) => typeof font.meta?.priority === "number" ? font.meta.priority === 0 : true).map((font) => {
|
|
6
|
+
let index = 0;
|
|
7
|
+
return {
|
|
8
|
+
...font,
|
|
9
|
+
src: font.src.map((source) => {
|
|
10
|
+
if ("name" in source) {
|
|
11
|
+
return source;
|
|
12
|
+
}
|
|
13
|
+
const proxied = {
|
|
14
|
+
...source,
|
|
15
|
+
originalURL: source.url,
|
|
16
|
+
url: urlProxy.proxy({
|
|
17
|
+
url: source.url,
|
|
18
|
+
// We only collect the first URL to avoid preloading fallback sources (eg. we only
|
|
19
|
+
// preload woff2 if woff is available)
|
|
20
|
+
collectPreload: index === 0,
|
|
21
|
+
data: {
|
|
22
|
+
weight: font.weight,
|
|
23
|
+
style: font.style
|
|
24
|
+
},
|
|
25
|
+
init: font.meta?.init ?? null
|
|
26
|
+
})
|
|
27
|
+
};
|
|
28
|
+
index++;
|
|
29
|
+
return proxied;
|
|
30
|
+
})
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
normalizeRemoteFontFaces
|
|
36
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type * as unifont from 'unifont';
|
|
2
|
+
import type { FontMetricsResolver, SystemFallbacksProvider } from '../definitions.js';
|
|
3
|
+
import type { FontFileData, ResolvedFontFamily } from '../types.js';
|
|
4
|
+
export interface CollectedFontForMetrics extends FontFileData {
|
|
5
|
+
data: Partial<unifont.FontFaceData>;
|
|
6
|
+
}
|
|
7
|
+
export declare function optimizeFallbacks({ family, fallbacks: _fallbacks, collectedFonts, enabled, systemFallbacksProvider, fontMetricsResolver, }: {
|
|
8
|
+
family: Pick<ResolvedFontFamily, 'name' | 'nameWithHash'>;
|
|
9
|
+
fallbacks: Array<string>;
|
|
10
|
+
collectedFonts: Array<CollectedFontForMetrics>;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
systemFallbacksProvider: SystemFallbacksProvider;
|
|
13
|
+
fontMetricsResolver: FontMetricsResolver;
|
|
14
|
+
}): Promise<null | {
|
|
15
|
+
css: string;
|
|
16
|
+
fallbacks: Array<string>;
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { isGenericFontFamily, unifontFontFaceDataToProperties } from "../utils.js";
|
|
2
|
+
async function optimizeFallbacks({
|
|
3
|
+
family,
|
|
4
|
+
fallbacks: _fallbacks,
|
|
5
|
+
collectedFonts,
|
|
6
|
+
enabled,
|
|
7
|
+
systemFallbacksProvider,
|
|
8
|
+
fontMetricsResolver
|
|
9
|
+
}) {
|
|
10
|
+
let fallbacks = [..._fallbacks];
|
|
11
|
+
if (fallbacks.length === 0 || !enabled || collectedFonts.length === 0) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const lastFallback = fallbacks[fallbacks.length - 1];
|
|
15
|
+
if (!isGenericFontFamily(lastFallback)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const localFonts = systemFallbacksProvider.getLocalFonts(lastFallback);
|
|
19
|
+
if (!localFonts || localFonts.length === 0) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (localFonts.includes(family.name)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const localFontsMappings = localFonts.map((font) => ({
|
|
26
|
+
font,
|
|
27
|
+
// We must't wrap in quote because that's handled by the CSS renderer
|
|
28
|
+
name: `${family.nameWithHash} fallback: ${font}`
|
|
29
|
+
}));
|
|
30
|
+
fallbacks = [...localFontsMappings.map((m) => m.name), ...fallbacks];
|
|
31
|
+
let css = "";
|
|
32
|
+
for (const { font, name } of localFontsMappings) {
|
|
33
|
+
for (const collected of collectedFonts) {
|
|
34
|
+
css += fontMetricsResolver.generateFontFace({
|
|
35
|
+
metrics: await fontMetricsResolver.getMetrics(family.name, collected),
|
|
36
|
+
fallbackMetrics: systemFallbacksProvider.getMetricsForLocalFont(font),
|
|
37
|
+
font,
|
|
38
|
+
name,
|
|
39
|
+
properties: unifontFontFaceDataToProperties(collected.data)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { css, fallbacks };
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
optimizeFallbacks
|
|
47
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Hasher, LocalProviderUrlResolver, RemoteFontProviderResolver } from '../definitions.js';
|
|
2
|
+
import type { FontFamily, ResolvedFontFamily } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Dedupes properties if applicable and resolves entrypoints.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveFamily({ family, hasher, remoteFontProviderResolver, localProviderUrlResolver, }: {
|
|
7
|
+
family: FontFamily;
|
|
8
|
+
hasher: Hasher;
|
|
9
|
+
remoteFontProviderResolver: RemoteFontProviderResolver;
|
|
10
|
+
localProviderUrlResolver: LocalProviderUrlResolver;
|
|
11
|
+
}): Promise<ResolvedFontFamily>;
|
|
12
|
+
/**
|
|
13
|
+
* A function for convenience. The actual logic lives in resolveFamily
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveFamilies({ families, ...dependencies }: {
|
|
16
|
+
families: Array<FontFamily>;
|
|
17
|
+
} & Omit<Parameters<typeof resolveFamily>[0], 'family'>): Promise<Array<ResolvedFontFamily>>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { LOCAL_PROVIDER_NAME } from "../constants.js";
|
|
2
|
+
import { dedupe, withoutQuotes } from "../utils.js";
|
|
3
|
+
function resolveVariants({
|
|
4
|
+
variants,
|
|
5
|
+
localProviderUrlResolver
|
|
6
|
+
}) {
|
|
7
|
+
return variants.map((variant) => ({
|
|
8
|
+
...variant,
|
|
9
|
+
weight: variant.weight.toString(),
|
|
10
|
+
src: variant.src.map((value) => {
|
|
11
|
+
const isValue = typeof value === "string" || value instanceof URL;
|
|
12
|
+
const url = (isValue ? value : value.url).toString();
|
|
13
|
+
const tech = isValue ? void 0 : value.tech;
|
|
14
|
+
return {
|
|
15
|
+
url: localProviderUrlResolver.resolve(url),
|
|
16
|
+
tech
|
|
17
|
+
};
|
|
18
|
+
})
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
async function resolveFamily({
|
|
22
|
+
family,
|
|
23
|
+
hasher,
|
|
24
|
+
remoteFontProviderResolver,
|
|
25
|
+
localProviderUrlResolver
|
|
26
|
+
}) {
|
|
27
|
+
const name = withoutQuotes(family.name);
|
|
28
|
+
const nameWithHash = `${name}-${hasher.hashObject(family)}`;
|
|
29
|
+
if (family.provider === LOCAL_PROVIDER_NAME) {
|
|
30
|
+
return {
|
|
31
|
+
...family,
|
|
32
|
+
name,
|
|
33
|
+
nameWithHash,
|
|
34
|
+
variants: resolveVariants({ variants: family.variants, localProviderUrlResolver }),
|
|
35
|
+
fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
...family,
|
|
40
|
+
name,
|
|
41
|
+
nameWithHash,
|
|
42
|
+
weights: family.weights ? dedupe(family.weights.map((weight) => weight.toString())) : void 0,
|
|
43
|
+
styles: family.styles ? dedupe(family.styles) : void 0,
|
|
44
|
+
subsets: family.subsets ? dedupe(family.subsets) : void 0,
|
|
45
|
+
fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0,
|
|
46
|
+
unicodeRange: family.unicodeRange ? dedupe(family.unicodeRange) : void 0,
|
|
47
|
+
// This will be Astro specific eventually
|
|
48
|
+
provider: await remoteFontProviderResolver.resolve(family.provider)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function resolveFamilies({
|
|
52
|
+
families,
|
|
53
|
+
...dependencies
|
|
54
|
+
}) {
|
|
55
|
+
return await Promise.all(
|
|
56
|
+
families.map(
|
|
57
|
+
(family) => resolveFamily({
|
|
58
|
+
family,
|
|
59
|
+
...dependencies
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
resolveFamilies,
|
|
66
|
+
resolveFamily
|
|
67
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Storage } from 'unstorage';
|
|
2
|
+
import type { CssRenderer, FontMetricsResolver, FontTypeExtractor, Hasher, LocalProviderUrlResolver, RemoteFontProviderResolver, SystemFallbacksProvider, UrlProxy } from './definitions.js';
|
|
3
|
+
import type { ConsumableMap, CreateUrlProxyParams, Defaults, FontFamily, FontFileDataMap } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Manages how fonts are resolved:
|
|
6
|
+
*
|
|
7
|
+
* - families are resolved
|
|
8
|
+
* - unifont providers are extracted from families
|
|
9
|
+
* - unifont is initialized
|
|
10
|
+
*
|
|
11
|
+
* For each family:
|
|
12
|
+
* - We create a URL proxy
|
|
13
|
+
* - We resolve the font and normalize the result
|
|
14
|
+
*
|
|
15
|
+
* For each resolved font:
|
|
16
|
+
* - We generate the CSS font face
|
|
17
|
+
* - We generate optimized fallbacks if applicable
|
|
18
|
+
* - We generate CSS variables
|
|
19
|
+
*
|
|
20
|
+
* Once that's done, the collected data is returned
|
|
21
|
+
*/
|
|
22
|
+
export declare function orchestrate({ families, hasher, remoteFontProviderResolver, localProviderUrlResolver, storage, cssRenderer, systemFallbacksProvider, fontMetricsResolver, fontTypeExtractor, createUrlProxy, defaults, }: {
|
|
23
|
+
families: Array<FontFamily>;
|
|
24
|
+
hasher: Hasher;
|
|
25
|
+
remoteFontProviderResolver: RemoteFontProviderResolver;
|
|
26
|
+
localProviderUrlResolver: LocalProviderUrlResolver;
|
|
27
|
+
storage: Storage;
|
|
28
|
+
cssRenderer: CssRenderer;
|
|
29
|
+
systemFallbacksProvider: SystemFallbacksProvider;
|
|
30
|
+
fontMetricsResolver: FontMetricsResolver;
|
|
31
|
+
fontTypeExtractor: FontTypeExtractor;
|
|
32
|
+
createUrlProxy: (params: CreateUrlProxyParams) => UrlProxy;
|
|
33
|
+
defaults: Defaults;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
fontFileDataMap: FontFileDataMap;
|
|
36
|
+
consumableMap: ConsumableMap;
|
|
37
|
+
}>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as unifont from "unifont";
|
|
2
|
+
import { LOCAL_PROVIDER_NAME } from "./constants.js";
|
|
3
|
+
import { extractUnifontProviders } from "./logic/extract-unifont-providers.js";
|
|
4
|
+
import { normalizeRemoteFontFaces } from "./logic/normalize-remote-font-faces.js";
|
|
5
|
+
import { optimizeFallbacks } from "./logic/optimize-fallbacks.js";
|
|
6
|
+
import { resolveFamilies } from "./logic/resolve-families.js";
|
|
7
|
+
import { resolveLocalFont } from "./providers/local.js";
|
|
8
|
+
import { pickFontFaceProperty, unifontFontFaceDataToProperties } from "./utils.js";
|
|
9
|
+
async function orchestrate({
|
|
10
|
+
families,
|
|
11
|
+
hasher,
|
|
12
|
+
remoteFontProviderResolver,
|
|
13
|
+
localProviderUrlResolver,
|
|
14
|
+
storage,
|
|
15
|
+
cssRenderer,
|
|
16
|
+
systemFallbacksProvider,
|
|
17
|
+
fontMetricsResolver,
|
|
18
|
+
fontTypeExtractor,
|
|
19
|
+
createUrlProxy,
|
|
20
|
+
defaults
|
|
21
|
+
}) {
|
|
22
|
+
let resolvedFamilies = await resolveFamilies({
|
|
23
|
+
families,
|
|
24
|
+
hasher,
|
|
25
|
+
remoteFontProviderResolver,
|
|
26
|
+
localProviderUrlResolver
|
|
27
|
+
});
|
|
28
|
+
const extractedUnifontProvidersResult = extractUnifontProviders({
|
|
29
|
+
families: resolvedFamilies,
|
|
30
|
+
hasher
|
|
31
|
+
});
|
|
32
|
+
resolvedFamilies = extractedUnifontProvidersResult.families;
|
|
33
|
+
const unifontProviders = extractedUnifontProvidersResult.providers;
|
|
34
|
+
const { resolveFont } = await unifont.createUnifont(unifontProviders, {
|
|
35
|
+
storage
|
|
36
|
+
});
|
|
37
|
+
const fontFileDataMap = /* @__PURE__ */ new Map();
|
|
38
|
+
const consumableMap = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const family of resolvedFamilies) {
|
|
40
|
+
const preloadData = [];
|
|
41
|
+
let css = "";
|
|
42
|
+
const collectedFonts = [];
|
|
43
|
+
const fallbacks = family.fallbacks ?? defaults.fallbacks ?? [];
|
|
44
|
+
const urlProxy = createUrlProxy({
|
|
45
|
+
local: family.provider === LOCAL_PROVIDER_NAME,
|
|
46
|
+
hasUrl: (hash) => fontFileDataMap.has(hash),
|
|
47
|
+
saveUrl: ({ hash, url, init }) => {
|
|
48
|
+
fontFileDataMap.set(hash, { url, init });
|
|
49
|
+
},
|
|
50
|
+
savePreload: (preload) => {
|
|
51
|
+
preloadData.push(preload);
|
|
52
|
+
},
|
|
53
|
+
saveFontData: (collected) => {
|
|
54
|
+
if (fallbacks && fallbacks.length > 0 && // If the same data has already been sent for this family, we don't want to have
|
|
55
|
+
// duplicated fallbacks. Such scenario can occur with unicode ranges.
|
|
56
|
+
!collectedFonts.some((f) => JSON.stringify(f.data) === JSON.stringify(collected.data))) {
|
|
57
|
+
collectedFonts.push(collected);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
let fonts;
|
|
62
|
+
if (family.provider === LOCAL_PROVIDER_NAME) {
|
|
63
|
+
const result = resolveLocalFont({
|
|
64
|
+
family,
|
|
65
|
+
urlProxy,
|
|
66
|
+
fontTypeExtractor
|
|
67
|
+
});
|
|
68
|
+
fonts = result.fonts;
|
|
69
|
+
} else {
|
|
70
|
+
const result = await resolveFont(
|
|
71
|
+
family.name,
|
|
72
|
+
// We do not merge the defaults, we only provide defaults as a fallback
|
|
73
|
+
{
|
|
74
|
+
weights: family.weights ?? defaults.weights,
|
|
75
|
+
styles: family.styles ?? defaults.styles,
|
|
76
|
+
subsets: family.subsets ?? defaults.subsets,
|
|
77
|
+
fallbacks: family.fallbacks ?? defaults.fallbacks
|
|
78
|
+
},
|
|
79
|
+
// By default, unifont goes through all providers. We use a different approach where
|
|
80
|
+
// we specify a provider per font. Name has been set while extracting unifont providers
|
|
81
|
+
// from families (inside extractUnifontProviders).
|
|
82
|
+
[family.provider.name]
|
|
83
|
+
);
|
|
84
|
+
fonts = normalizeRemoteFontFaces({ fonts: result.fonts, urlProxy });
|
|
85
|
+
}
|
|
86
|
+
for (const data of fonts) {
|
|
87
|
+
css += cssRenderer.generateFontFace(
|
|
88
|
+
family.nameWithHash,
|
|
89
|
+
unifontFontFaceDataToProperties({
|
|
90
|
+
src: data.src,
|
|
91
|
+
weight: data.weight,
|
|
92
|
+
style: data.style,
|
|
93
|
+
// User settings override the generated font settings. We use a helper function
|
|
94
|
+
// because local and remote providers store this data in different places.
|
|
95
|
+
display: pickFontFaceProperty("display", { data, family }),
|
|
96
|
+
unicodeRange: pickFontFaceProperty("unicodeRange", { data, family }),
|
|
97
|
+
stretch: pickFontFaceProperty("stretch", { data, family }),
|
|
98
|
+
featureSettings: pickFontFaceProperty("featureSettings", { data, family }),
|
|
99
|
+
variationSettings: pickFontFaceProperty("variationSettings", { data, family })
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const cssVarValues = [family.nameWithHash];
|
|
104
|
+
const optimizeFallbacksResult = await optimizeFallbacks({
|
|
105
|
+
family,
|
|
106
|
+
fallbacks,
|
|
107
|
+
collectedFonts,
|
|
108
|
+
enabled: family.optimizedFallbacks ?? defaults.optimizedFallbacks ?? false,
|
|
109
|
+
systemFallbacksProvider,
|
|
110
|
+
fontMetricsResolver
|
|
111
|
+
});
|
|
112
|
+
if (optimizeFallbacksResult) {
|
|
113
|
+
css += optimizeFallbacksResult.css;
|
|
114
|
+
cssVarValues.push(...optimizeFallbacksResult.fallbacks);
|
|
115
|
+
} else {
|
|
116
|
+
cssVarValues.push(...fallbacks);
|
|
117
|
+
}
|
|
118
|
+
css += cssRenderer.generateCssVariable(family.cssVariable, cssVarValues);
|
|
119
|
+
consumableMap.set(family.cssVariable, { preloadData, css });
|
|
120
|
+
}
|
|
121
|
+
return { fontFileDataMap, consumableMap };
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
orchestrate
|
|
125
|
+
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type * as unifont from 'unifont';
|
|
2
|
+
import type { FontTypeExtractor, UrlProxy } from '../definitions.js';
|
|
2
3
|
import type { ResolvedLocalFontFamily } from '../types.js';
|
|
3
|
-
type InitializedProvider = NonNullable<Awaited<ReturnType<unifont.Provider>>>;
|
|
4
|
-
type ResolveFontResult = NonNullable<Awaited<ReturnType<InitializedProvider['resolveFont']>>>;
|
|
5
4
|
interface Options {
|
|
6
5
|
family: ResolvedLocalFontFamily;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
data: Partial<unifont.FontFaceData>;
|
|
10
|
-
}) => string;
|
|
6
|
+
urlProxy: UrlProxy;
|
|
7
|
+
fontTypeExtractor: FontTypeExtractor;
|
|
11
8
|
}
|
|
12
|
-
export declare function resolveLocalFont({ family,
|
|
9
|
+
export declare function resolveLocalFont({ family, urlProxy, fontTypeExtractor }: Options): {
|
|
10
|
+
fonts: Array<unifont.FontFaceData>;
|
|
11
|
+
};
|
|
13
12
|
export {};
|