fontaine 0.4.1 → 0.6.0
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/README.md +9 -10
- package/dist/index.cjs +201 -152
- package/dist/index.d.cts +88 -16
- package/dist/index.d.mts +88 -16
- package/dist/index.d.ts +88 -16
- package/dist/index.mjs +204 -155
- package/package.json +48 -40
package/dist/index.d.mts
CHANGED
|
@@ -1,32 +1,104 @@
|
|
|
1
|
-
import * as unplugin from 'unplugin';
|
|
2
1
|
import { Font } from '@capsizecss/unpack';
|
|
2
|
+
import * as unplugin from 'unplugin';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates a fallback name based on the first font family specified in the input string.
|
|
6
|
+
* @param {string} name - The full font family string.
|
|
7
|
+
* @returns {string} - The fallback font name.
|
|
8
|
+
*/
|
|
9
|
+
declare function generateFallbackName(name: string): string;
|
|
10
|
+
interface FallbackOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The name of the fallback font.
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
/**
|
|
16
|
+
* The fallback font family name.
|
|
17
|
+
*/
|
|
18
|
+
font: string;
|
|
19
|
+
/**
|
|
20
|
+
* Metrics for fallback face calculations.
|
|
21
|
+
* @optional
|
|
22
|
+
*/
|
|
23
|
+
metrics?: FontFaceMetrics;
|
|
24
|
+
/**
|
|
25
|
+
* Additional properties that may be included dynamically
|
|
26
|
+
*/
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
}
|
|
29
|
+
type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
|
|
30
|
+
/**
|
|
31
|
+
* Generates a CSS `@font-face' declaration for a font, taking fallback and resizing into account.
|
|
32
|
+
* @param {FontFaceMetrics} metrics - The metrics of the preferred font. See {@link FontFaceMetrics}.
|
|
33
|
+
* @param {FallbackOptions} fallback - The fallback options, including name, font and optional metrics. See {@link FallbackOptions}.
|
|
34
|
+
* @returns {string} - The full `@font-face` CSS declaration.
|
|
35
|
+
*/
|
|
36
|
+
declare function generateFontFace(metrics: FontFaceMetrics, fallback: FallbackOptions): string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves the font metrics for a given font family from the metrics collection. Uses caching to avoid redundant calculations.
|
|
40
|
+
* @param {string} family - The name of the font family for which metrics are requested.
|
|
41
|
+
* @returns {Promise<FontFaceMetrics | null>} - A promise that resolves with the filtered font metrics or null if not found. See {@link FontFaceMetrics}.
|
|
42
|
+
* @async
|
|
43
|
+
*/
|
|
44
|
+
declare function getMetricsForFamily(family: string): Promise<FontFaceMetrics | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Reads font metrics from a specified source URL or file path. This function supports both local files and remote URLs.
|
|
47
|
+
* It caches the results to optimise subsequent requests for the same source.
|
|
48
|
+
* @param {URL | string} _source - The source URL or local file path from which to read the font metrics.
|
|
49
|
+
* @returns {Promise<FontFaceMetrics | null>} - A promise that resolves to the filtered font metrics or null if the source cannot be processed.
|
|
50
|
+
* @async
|
|
51
|
+
*/
|
|
52
|
+
declare function readMetrics(_source: URL | string): Promise<FontFaceMetrics | null>;
|
|
3
53
|
|
|
4
54
|
interface FontaineTransformOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Configuration options for the CSS transformation.
|
|
57
|
+
* @optional
|
|
58
|
+
*/
|
|
5
59
|
css?: {
|
|
60
|
+
/**
|
|
61
|
+
* Holds the current value of the CSS being transformed.
|
|
62
|
+
* @optional
|
|
63
|
+
*/
|
|
6
64
|
value?: string;
|
|
7
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* An array of fallback font family names to use.
|
|
68
|
+
*/
|
|
8
69
|
fallbacks: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Function to resolve a given path to a valid URL or local path.
|
|
72
|
+
* This is typically used to resolve font file paths.
|
|
73
|
+
* @optional
|
|
74
|
+
*/
|
|
9
75
|
resolvePath?: (path: string) => string | URL;
|
|
76
|
+
/**
|
|
77
|
+
* A function to determine whether to skip font face generation for a given fallback name.
|
|
78
|
+
* @optional
|
|
79
|
+
*/
|
|
10
80
|
skipFontFaceGeneration?: (fallbackName: string) => boolean;
|
|
11
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Function to generate an unquoted font family name to use as a fallback.
|
|
83
|
+
* This should return a valid CSS font family name and should not include quotes.
|
|
84
|
+
* @optional
|
|
85
|
+
*/
|
|
12
86
|
fallbackName?: (name: string) => string;
|
|
13
87
|
/** @deprecated use fallbackName */
|
|
14
88
|
overrideName?: (name: string) => string;
|
|
89
|
+
/**
|
|
90
|
+
* Specifies whether to create a source map for the transformation.
|
|
91
|
+
* @optional
|
|
92
|
+
*/
|
|
15
93
|
sourcemap?: boolean;
|
|
16
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Transforms CSS files to include font fallbacks.
|
|
97
|
+
*
|
|
98
|
+
* @param options - The transformation options. See {@link FontaineTransformOptions}.
|
|
99
|
+
* @returns The unplugin instance.
|
|
100
|
+
*/
|
|
17
101
|
declare const FontaineTransform: unplugin.UnpluginInstance<FontaineTransformOptions, boolean>;
|
|
18
102
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
name: string;
|
|
22
|
-
font: string;
|
|
23
|
-
metrics?: FontFaceMetrics;
|
|
24
|
-
[key: string]: any;
|
|
25
|
-
}
|
|
26
|
-
type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
|
|
27
|
-
declare const generateFontFace: (metrics: FontFaceMetrics, fallback: FallbackOptions) => string;
|
|
28
|
-
|
|
29
|
-
declare function getMetricsForFamily(family: string): Promise<FontFaceMetrics | null>;
|
|
30
|
-
declare function readMetrics(_source: URL | string): Promise<FontFaceMetrics | null>;
|
|
31
|
-
|
|
32
|
-
export { FontaineTransform, type FontaineTransformOptions, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
|
|
103
|
+
export { FontaineTransform, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
|
|
104
|
+
export type { FontaineTransformOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,32 +1,104 @@
|
|
|
1
|
-
import * as unplugin from 'unplugin';
|
|
2
1
|
import { Font } from '@capsizecss/unpack';
|
|
2
|
+
import * as unplugin from 'unplugin';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates a fallback name based on the first font family specified in the input string.
|
|
6
|
+
* @param {string} name - The full font family string.
|
|
7
|
+
* @returns {string} - The fallback font name.
|
|
8
|
+
*/
|
|
9
|
+
declare function generateFallbackName(name: string): string;
|
|
10
|
+
interface FallbackOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The name of the fallback font.
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
/**
|
|
16
|
+
* The fallback font family name.
|
|
17
|
+
*/
|
|
18
|
+
font: string;
|
|
19
|
+
/**
|
|
20
|
+
* Metrics for fallback face calculations.
|
|
21
|
+
* @optional
|
|
22
|
+
*/
|
|
23
|
+
metrics?: FontFaceMetrics;
|
|
24
|
+
/**
|
|
25
|
+
* Additional properties that may be included dynamically
|
|
26
|
+
*/
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
}
|
|
29
|
+
type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
|
|
30
|
+
/**
|
|
31
|
+
* Generates a CSS `@font-face' declaration for a font, taking fallback and resizing into account.
|
|
32
|
+
* @param {FontFaceMetrics} metrics - The metrics of the preferred font. See {@link FontFaceMetrics}.
|
|
33
|
+
* @param {FallbackOptions} fallback - The fallback options, including name, font and optional metrics. See {@link FallbackOptions}.
|
|
34
|
+
* @returns {string} - The full `@font-face` CSS declaration.
|
|
35
|
+
*/
|
|
36
|
+
declare function generateFontFace(metrics: FontFaceMetrics, fallback: FallbackOptions): string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves the font metrics for a given font family from the metrics collection. Uses caching to avoid redundant calculations.
|
|
40
|
+
* @param {string} family - The name of the font family for which metrics are requested.
|
|
41
|
+
* @returns {Promise<FontFaceMetrics | null>} - A promise that resolves with the filtered font metrics or null if not found. See {@link FontFaceMetrics}.
|
|
42
|
+
* @async
|
|
43
|
+
*/
|
|
44
|
+
declare function getMetricsForFamily(family: string): Promise<FontFaceMetrics | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Reads font metrics from a specified source URL or file path. This function supports both local files and remote URLs.
|
|
47
|
+
* It caches the results to optimise subsequent requests for the same source.
|
|
48
|
+
* @param {URL | string} _source - The source URL or local file path from which to read the font metrics.
|
|
49
|
+
* @returns {Promise<FontFaceMetrics | null>} - A promise that resolves to the filtered font metrics or null if the source cannot be processed.
|
|
50
|
+
* @async
|
|
51
|
+
*/
|
|
52
|
+
declare function readMetrics(_source: URL | string): Promise<FontFaceMetrics | null>;
|
|
3
53
|
|
|
4
54
|
interface FontaineTransformOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Configuration options for the CSS transformation.
|
|
57
|
+
* @optional
|
|
58
|
+
*/
|
|
5
59
|
css?: {
|
|
60
|
+
/**
|
|
61
|
+
* Holds the current value of the CSS being transformed.
|
|
62
|
+
* @optional
|
|
63
|
+
*/
|
|
6
64
|
value?: string;
|
|
7
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* An array of fallback font family names to use.
|
|
68
|
+
*/
|
|
8
69
|
fallbacks: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Function to resolve a given path to a valid URL or local path.
|
|
72
|
+
* This is typically used to resolve font file paths.
|
|
73
|
+
* @optional
|
|
74
|
+
*/
|
|
9
75
|
resolvePath?: (path: string) => string | URL;
|
|
76
|
+
/**
|
|
77
|
+
* A function to determine whether to skip font face generation for a given fallback name.
|
|
78
|
+
* @optional
|
|
79
|
+
*/
|
|
10
80
|
skipFontFaceGeneration?: (fallbackName: string) => boolean;
|
|
11
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Function to generate an unquoted font family name to use as a fallback.
|
|
83
|
+
* This should return a valid CSS font family name and should not include quotes.
|
|
84
|
+
* @optional
|
|
85
|
+
*/
|
|
12
86
|
fallbackName?: (name: string) => string;
|
|
13
87
|
/** @deprecated use fallbackName */
|
|
14
88
|
overrideName?: (name: string) => string;
|
|
89
|
+
/**
|
|
90
|
+
* Specifies whether to create a source map for the transformation.
|
|
91
|
+
* @optional
|
|
92
|
+
*/
|
|
15
93
|
sourcemap?: boolean;
|
|
16
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Transforms CSS files to include font fallbacks.
|
|
97
|
+
*
|
|
98
|
+
* @param options - The transformation options. See {@link FontaineTransformOptions}.
|
|
99
|
+
* @returns The unplugin instance.
|
|
100
|
+
*/
|
|
17
101
|
declare const FontaineTransform: unplugin.UnpluginInstance<FontaineTransformOptions, boolean>;
|
|
18
102
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
name: string;
|
|
22
|
-
font: string;
|
|
23
|
-
metrics?: FontFaceMetrics;
|
|
24
|
-
[key: string]: any;
|
|
25
|
-
}
|
|
26
|
-
type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
|
|
27
|
-
declare const generateFontFace: (metrics: FontFaceMetrics, fallback: FallbackOptions) => string;
|
|
28
|
-
|
|
29
|
-
declare function getMetricsForFamily(family: string): Promise<FontFaceMetrics | null>;
|
|
30
|
-
declare function readMetrics(_source: URL | string): Promise<FontFaceMetrics | null>;
|
|
31
|
-
|
|
32
|
-
export { FontaineTransform, type FontaineTransformOptions, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
|
|
103
|
+
export { FontaineTransform, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
|
|
104
|
+
export type { FontaineTransformOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,40 +1,109 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createRegExp, charIn, exactly,
|
|
3
|
-
import
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { fromFile, fromUrl } from '@capsizecss/unpack';
|
|
1
|
+
import { parse, walk, generate } from 'css-tree';
|
|
2
|
+
import { createRegExp, charIn, exactly, anyOf } from 'magic-regexp';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
4
|
import { fontFamilyToCamelCase } from '@capsizecss/metrics';
|
|
5
|
+
import { fromFile, fromUrl } from '@capsizecss/unpack';
|
|
7
6
|
import { parseURL } from 'ufo';
|
|
8
|
-
import
|
|
7
|
+
import MagicString from 'magic-string';
|
|
8
|
+
import { isAbsolute } from 'pathe';
|
|
9
|
+
import { createUnplugin } from 'unplugin';
|
|
9
10
|
|
|
10
|
-
function
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
function toPercentage(value, fractionDigits = 4) {
|
|
12
|
+
const percentage = value * 100;
|
|
13
|
+
return `${+percentage.toFixed(fractionDigits)}%`;
|
|
14
|
+
}
|
|
15
|
+
function toCSS(properties, indent = 2) {
|
|
16
|
+
return Object.entries(properties).map(([key, value]) => `${" ".repeat(indent)}${key}: ${value};`).join("\n");
|
|
17
|
+
}
|
|
18
|
+
const QUOTES_RE = createRegExp(
|
|
19
|
+
charIn(`"'`).at.lineStart().or(charIn(`"'`).at.lineEnd()),
|
|
20
|
+
["g"]
|
|
21
|
+
);
|
|
22
|
+
const withoutQuotes = (str) => str.trim().replace(QUOTES_RE, "");
|
|
23
|
+
const genericCSSFamilies = /* @__PURE__ */ new Set([
|
|
24
|
+
"serif",
|
|
25
|
+
"sans-serif",
|
|
26
|
+
"monospace",
|
|
27
|
+
"cursive",
|
|
28
|
+
"fantasy",
|
|
29
|
+
"system-ui",
|
|
30
|
+
"ui-serif",
|
|
31
|
+
"ui-sans-serif",
|
|
32
|
+
"ui-monospace",
|
|
33
|
+
"ui-rounded",
|
|
34
|
+
"emoji",
|
|
35
|
+
"math",
|
|
36
|
+
"fangsong"
|
|
37
|
+
]);
|
|
38
|
+
const fontProperties = /* @__PURE__ */ new Set(["font-weight", "font-style", "font-stretch"]);
|
|
39
|
+
function parseFontFace(css) {
|
|
40
|
+
const families = [];
|
|
41
|
+
const ast = typeof css === "string" ? parse(css, { positions: true }) : css;
|
|
42
|
+
walk(ast, {
|
|
43
|
+
visit: "Atrule",
|
|
44
|
+
enter(node) {
|
|
45
|
+
if (node.name !== "font-face")
|
|
46
|
+
return;
|
|
47
|
+
let family;
|
|
48
|
+
const sources = [];
|
|
49
|
+
const properties = {};
|
|
50
|
+
if (node.block) {
|
|
51
|
+
walk(node.block, {
|
|
52
|
+
visit: "Declaration",
|
|
53
|
+
enter(declaration) {
|
|
54
|
+
if (declaration.property === "font-family" && declaration.value.type === "Value") {
|
|
55
|
+
for (const child of declaration.value.children) {
|
|
56
|
+
if (child.type === "String") {
|
|
57
|
+
family = withoutQuotes(child.value);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
if (child.type === "Identifier" && !genericCSSFamilies.has(child.name)) {
|
|
61
|
+
family = child.name;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (fontProperties.has(declaration.property)) {
|
|
67
|
+
if (declaration.value.type === "Value") {
|
|
68
|
+
for (const child of declaration.value.children) {
|
|
69
|
+
const hasValue = !!properties[declaration.property];
|
|
70
|
+
properties[declaration.property] ||= "";
|
|
71
|
+
properties[declaration.property] += (hasValue ? " " : "") + generate(child);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (declaration.property === "src") {
|
|
76
|
+
walk(declaration.value, {
|
|
77
|
+
visit: "Url",
|
|
78
|
+
enter(urlNode) {
|
|
79
|
+
const source = withoutQuotes(urlNode.value);
|
|
80
|
+
if (source) {
|
|
81
|
+
sources.push(source);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (family) {
|
|
90
|
+
for (const source of sources) {
|
|
91
|
+
families.push({ index: node.loc.start.offset, family, source, properties });
|
|
92
|
+
}
|
|
93
|
+
if (!sources.length) {
|
|
94
|
+
families.push({ index: node.loc.start.offset, family, properties });
|
|
20
95
|
}
|
|
21
96
|
}
|
|
22
97
|
}
|
|
23
|
-
}
|
|
24
|
-
|
|
98
|
+
});
|
|
99
|
+
return families;
|
|
25
100
|
}
|
|
26
|
-
|
|
101
|
+
function generateFallbackName(name) {
|
|
27
102
|
const firstFamily = withoutQuotes(name.split(",").shift());
|
|
28
103
|
return `${firstFamily} fallback`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const {
|
|
33
|
-
name: fallbackName,
|
|
34
|
-
font: fallbackFontName,
|
|
35
|
-
metrics: fallbackMetrics,
|
|
36
|
-
...properties
|
|
37
|
-
} = fallback;
|
|
104
|
+
}
|
|
105
|
+
function generateFontFace(metrics, fallback) {
|
|
106
|
+
const { name: fallbackName, font: fallbackFontName, metrics: fallbackMetrics, ...properties } = fallback;
|
|
38
107
|
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
|
|
39
108
|
const fallbackFontXAvgRatio = fallbackMetrics ? fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm : 1;
|
|
40
109
|
const sizeAdjust = fallbackMetrics && preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1;
|
|
@@ -44,7 +113,7 @@ const generateFontFace = (metrics, fallback) => {
|
|
|
44
113
|
const lineGapOverride = metrics.lineGap / adjustedEmSquare;
|
|
45
114
|
const declaration = {
|
|
46
115
|
"font-family": JSON.stringify(fallbackName),
|
|
47
|
-
src: `local(${JSON.stringify(fallbackFontName)})`,
|
|
116
|
+
"src": `local(${JSON.stringify(fallbackFontName)})`,
|
|
48
117
|
"size-adjust": toPercentage(sizeAdjust),
|
|
49
118
|
"ascent-override": toPercentage(ascentOverride),
|
|
50
119
|
"descent-override": toPercentage(descentOverride),
|
|
@@ -55,50 +124,30 @@ const generateFontFace = (metrics, fallback) => {
|
|
|
55
124
|
${toCSS(declaration)}
|
|
56
125
|
}
|
|
57
126
|
`;
|
|
58
|
-
}
|
|
59
|
-
const toPercentage = (value, fractionDigits = 4) => {
|
|
60
|
-
const percentage = value * 100;
|
|
61
|
-
return +percentage.toFixed(fractionDigits) + "%";
|
|
62
|
-
};
|
|
63
|
-
const toCSS = (properties, indent = 2) => Object.entries(properties).map(([key, value]) => " ".repeat(indent) + `${key}: ${value};`).join("\n");
|
|
64
|
-
const QUOTES_RE = createRegExp(
|
|
65
|
-
charIn(`"'`).at.lineStart().or(charIn(`"'`).at.lineEnd()),
|
|
66
|
-
["g"]
|
|
67
|
-
);
|
|
68
|
-
const FAMILY_RE = createRegExp(
|
|
69
|
-
exactly("font-family:").and(whitespace.optionally()).and(charNotIn(";}").times.any().as("fontFamily"))
|
|
70
|
-
);
|
|
71
|
-
const SOURCE_RE = createRegExp(
|
|
72
|
-
exactly("src:").and(whitespace.optionally()).and(charNotIn(";}").times.any().as("src")),
|
|
73
|
-
["g"]
|
|
74
|
-
);
|
|
75
|
-
const URL_RE = createRegExp(
|
|
76
|
-
exactly("url(").and(charNotIn(")").times.any().as("url")).and(")"),
|
|
77
|
-
["g"]
|
|
78
|
-
);
|
|
127
|
+
}
|
|
79
128
|
|
|
80
129
|
const metricCache = {};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
lineGap,
|
|
91
|
-
unitsPerEm,
|
|
92
|
-
xWidthAvg
|
|
93
|
-
});
|
|
130
|
+
function filterRequiredMetrics({ ascent, descent, lineGap, unitsPerEm, xWidthAvg }) {
|
|
131
|
+
return {
|
|
132
|
+
ascent,
|
|
133
|
+
descent,
|
|
134
|
+
lineGap,
|
|
135
|
+
unitsPerEm,
|
|
136
|
+
xWidthAvg
|
|
137
|
+
};
|
|
138
|
+
}
|
|
94
139
|
async function getMetricsForFamily(family) {
|
|
95
140
|
family = withoutQuotes(family);
|
|
96
141
|
if (family in metricCache)
|
|
97
142
|
return metricCache[family];
|
|
98
143
|
try {
|
|
99
144
|
const name = fontFamilyToCamelCase(family);
|
|
100
|
-
const { entireMetricsCollection } = await import('@capsizecss/metrics/entireMetricsCollection
|
|
145
|
+
const { entireMetricsCollection } = await import('@capsizecss/metrics/entireMetricsCollection');
|
|
101
146
|
const metrics = entireMetricsCollection[name];
|
|
147
|
+
if (!("descent" in metrics)) {
|
|
148
|
+
metricCache[family] = null;
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
102
151
|
const filteredMetrics = filterRequiredMetrics(metrics);
|
|
103
152
|
metricCache[family] = filteredMetrics;
|
|
104
153
|
return filteredMetrics;
|
|
@@ -107,113 +156,113 @@ async function getMetricsForFamily(family) {
|
|
|
107
156
|
return null;
|
|
108
157
|
}
|
|
109
158
|
}
|
|
159
|
+
const urlRequestCache = /* @__PURE__ */ new Map();
|
|
110
160
|
async function readMetrics(_source) {
|
|
111
161
|
const source = typeof _source !== "string" && "href" in _source ? _source.href : _source;
|
|
112
|
-
if (source in metricCache)
|
|
113
|
-
return
|
|
114
|
-
}
|
|
162
|
+
if (source in metricCache)
|
|
163
|
+
return metricCache[source];
|
|
115
164
|
const { protocol } = parseURL(source);
|
|
116
165
|
if (!protocol)
|
|
117
166
|
return null;
|
|
118
|
-
|
|
167
|
+
let metrics;
|
|
168
|
+
if (protocol === "file:") {
|
|
169
|
+
metrics = await fromFile(fileURLToPath(source));
|
|
170
|
+
} else {
|
|
171
|
+
if (urlRequestCache.has(source)) {
|
|
172
|
+
metrics = await urlRequestCache.get(source);
|
|
173
|
+
} else {
|
|
174
|
+
const requestPromise = fromUrl(source);
|
|
175
|
+
urlRequestCache.set(source, requestPromise);
|
|
176
|
+
metrics = await requestPromise;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
119
179
|
const filteredMetrics = filterRequiredMetrics(metrics);
|
|
120
180
|
metricCache[source] = filteredMetrics;
|
|
121
181
|
return filteredMetrics;
|
|
122
182
|
}
|
|
123
183
|
|
|
124
184
|
const supportedExtensions = ["woff2", "woff", "ttf"];
|
|
125
|
-
const
|
|
126
|
-
(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
185
|
+
const CSS_RE = createRegExp(
|
|
186
|
+
exactly(".").and(anyOf("sass", "css", "scss")).at.lineEnd()
|
|
187
|
+
);
|
|
188
|
+
const RELATIVE_RE = createRegExp(
|
|
189
|
+
exactly(".").or("..").and(anyOf("/", "\\")).at.lineStart()
|
|
190
|
+
);
|
|
191
|
+
const FontaineTransform = createUnplugin((options) => {
|
|
192
|
+
const cssContext = options.css = options.css || {};
|
|
193
|
+
cssContext.value = "";
|
|
194
|
+
const resolvePath = options.resolvePath || ((id) => id);
|
|
195
|
+
const fallbackName = options.fallbackName || options.overrideName || generateFallbackName;
|
|
196
|
+
const skipFontFaceGeneration = options.skipFontFaceGeneration || (() => false);
|
|
197
|
+
function readMetricsFromId(path, importer) {
|
|
198
|
+
const resolvedPath = isAbsolute(importer) && RELATIVE_RE.test(path) ? new URL(path, pathToFileURL(importer)) : resolvePath(path);
|
|
199
|
+
return readMetrics(resolvedPath);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
name: "fontaine-transform",
|
|
203
|
+
enforce: "pre",
|
|
204
|
+
transformInclude(id) {
|
|
205
|
+
const { pathname } = parseURL(id);
|
|
206
|
+
return CSS_RE.test(pathname) || CSS_RE.test(id);
|
|
207
|
+
},
|
|
208
|
+
async transform(code, id) {
|
|
209
|
+
const s = new MagicString(code);
|
|
210
|
+
const ast = parse(code, { positions: true });
|
|
211
|
+
for (const { family, source, index, properties } of parseFontFace(ast)) {
|
|
212
|
+
if (!supportedExtensions.some((e) => source?.endsWith(e)))
|
|
213
|
+
continue;
|
|
214
|
+
if (skipFontFaceGeneration(fallbackName(family)))
|
|
215
|
+
continue;
|
|
216
|
+
const metrics = await getMetricsForFamily(family) || source && await readMetricsFromId(source, id).catch(() => null);
|
|
217
|
+
if (!metrics)
|
|
218
|
+
continue;
|
|
219
|
+
for (let i = options.fallbacks.length - 1; i >= 0; i--) {
|
|
220
|
+
const fallback = options.fallbacks[i];
|
|
221
|
+
const fallbackMetrics = await getMetricsForFamily(fallback);
|
|
222
|
+
if (!fallbackMetrics)
|
|
149
223
|
continue;
|
|
150
|
-
|
|
151
|
-
|
|
224
|
+
const fontFace = generateFontFace(metrics, {
|
|
225
|
+
name: fallbackName(family),
|
|
226
|
+
font: fallback,
|
|
227
|
+
metrics: fallbackMetrics,
|
|
228
|
+
...properties
|
|
229
|
+
});
|
|
230
|
+
cssContext.value += fontFace;
|
|
231
|
+
s.appendLeft(index, fontFace);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
walk(ast, {
|
|
235
|
+
visit: "Declaration",
|
|
236
|
+
enter(node) {
|
|
237
|
+
if (node.property !== "font-family")
|
|
238
|
+
return;
|
|
239
|
+
if (this.atrule && this.atrule.name === "font-face")
|
|
240
|
+
return;
|
|
241
|
+
if (node.value.type !== "Value")
|
|
242
|
+
return;
|
|
243
|
+
for (const child of node.value.children) {
|
|
244
|
+
let family;
|
|
245
|
+
if (child.type === "String") {
|
|
246
|
+
family = withoutQuotes(child.value);
|
|
247
|
+
} else if (child.type === "Identifier" && child.name !== "inherit") {
|
|
248
|
+
family = child.name;
|
|
249
|
+
}
|
|
152
250
|
if (!family)
|
|
153
251
|
continue;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (skipFontFaceGeneration(fallbackName(family)))
|
|
157
|
-
continue;
|
|
158
|
-
const metrics = await getMetricsForFamily(family) || source && await readMetricsFromId(source, id).catch(() => null);
|
|
159
|
-
if (!metrics) {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
for (let i = options.fallbacks.length - 1; i >= 0; i--) {
|
|
163
|
-
const fallback = options.fallbacks[i];
|
|
164
|
-
const fallbackMetrics = await getMetricsForFamily(fallback);
|
|
165
|
-
if (!fallbackMetrics) {
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
const fontFace = generateFontFace(metrics, {
|
|
169
|
-
name: fallbackName(family),
|
|
170
|
-
font: fallback,
|
|
171
|
-
metrics: fallbackMetrics
|
|
172
|
-
});
|
|
173
|
-
cssContext.value += fontFace;
|
|
174
|
-
s.appendLeft(match.index, fontFace);
|
|
175
|
-
}
|
|
252
|
+
s.appendRight(child.loc.end.offset, `, "${fallbackName(family)}"`);
|
|
253
|
+
return;
|
|
176
254
|
}
|
|
177
255
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (!families.length || families[0] === "inherit")
|
|
186
|
-
continue;
|
|
187
|
-
s.overwrite(
|
|
188
|
-
index,
|
|
189
|
-
index + matchContent.length,
|
|
190
|
-
" " + [
|
|
191
|
-
families[0],
|
|
192
|
-
`"${generateFallbackName(families[0])}"`,
|
|
193
|
-
...families.slice(1)
|
|
194
|
-
].join(", ")
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
if (s.hasChanged()) {
|
|
198
|
-
return {
|
|
199
|
-
code: s.toString(),
|
|
200
|
-
map: options.sourcemap ? s.generateMap({ source: id, includeContent: true }) : void 0
|
|
201
|
-
};
|
|
202
|
-
}
|
|
256
|
+
});
|
|
257
|
+
if (s.hasChanged()) {
|
|
258
|
+
return {
|
|
259
|
+
code: s.toString(),
|
|
260
|
+
/* v8 ignore next 3 */
|
|
261
|
+
map: options.sourcemap ? s.generateMap({ source: id, includeContent: true }) : void 0
|
|
262
|
+
};
|
|
203
263
|
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
);
|
|
207
|
-
const FONT_FACE_RE = createRegExp(
|
|
208
|
-
exactly("@font-face").and(whitespace.times.any()).and("{").and(charNotIn("}").times.any()).and("}"),
|
|
209
|
-
["g"]
|
|
210
|
-
);
|
|
211
|
-
const FONT_FAMILY_RE = createRegExp(
|
|
212
|
-
charNotIn(";}").times.any().as("family").after(exactly("font-family:").and(whitespace.times.any())).before(charIn(";}").or(exactly("").at.lineEnd())),
|
|
213
|
-
["g"]
|
|
214
|
-
);
|
|
215
|
-
const CSS_RE = createRegExp(
|
|
216
|
-
exactly(".").and(anyOf("sass", "css", "scss")).at.lineEnd()
|
|
217
|
-
);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
});
|
|
218
267
|
|
|
219
268
|
export { FontaineTransform, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
|