fontaine 0.2.3 → 0.3.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 +16 -0
- package/dist/index.cjs +31 -18
- package/dist/index.d.ts +9 -28
- package/dist/index.mjs +31 -18
- package/package.json +22 -25
package/README.md
CHANGED
|
@@ -95,6 +95,22 @@ exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
|
|
|
95
95
|
config.plugins.push(FontaineTransform.webpack(options))
|
|
96
96
|
actions.replaceWebpackConfig(config)
|
|
97
97
|
}
|
|
98
|
+
|
|
99
|
+
// Astro config - astro.config.mjs
|
|
100
|
+
import { defineConfig } from 'astro/config'
|
|
101
|
+
import { FontaineTransform } from 'fontaine'
|
|
102
|
+
|
|
103
|
+
export default defineConfig({
|
|
104
|
+
integrations: [],
|
|
105
|
+
vite: {
|
|
106
|
+
plugins: [
|
|
107
|
+
FontaineTransform.vite({
|
|
108
|
+
fallbacks: ['Arial'],
|
|
109
|
+
resolvePath: (id) => new URL(`./public${id}`, import.meta.url), // id is the font src value in the CSS
|
|
110
|
+
}),
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
})
|
|
98
114
|
```
|
|
99
115
|
|
|
100
116
|
> **Note**
|
package/dist/index.cjs
CHANGED
|
@@ -5,16 +5,16 @@ const magicRegexp = require('magic-regexp');
|
|
|
5
5
|
const MagicString = require('magic-string');
|
|
6
6
|
const node_url = require('node:url');
|
|
7
7
|
const unpack = require('@capsizecss/unpack');
|
|
8
|
+
const metrics = require('@capsizecss/metrics');
|
|
8
9
|
const ufo = require('ufo');
|
|
9
|
-
const scule = require('scule');
|
|
10
10
|
const pathe = require('pathe');
|
|
11
11
|
|
|
12
12
|
function* parseFontFace(css) {
|
|
13
13
|
const fontFamily = css.match(FAMILY_RE)?.groups.fontFamily;
|
|
14
14
|
const family = withoutQuotes(fontFamily?.split(",")[0] || "");
|
|
15
15
|
for (const match of css.matchAll(SOURCE_RE)) {
|
|
16
|
-
const sources = match.groups.src?.split(",")
|
|
17
|
-
for (const entry of sources) {
|
|
16
|
+
const sources = match.groups.src?.split(",");
|
|
17
|
+
for (const entry of sources || []) {
|
|
18
18
|
for (const url of entry.matchAll(URL_RE)) {
|
|
19
19
|
const source = withoutQuotes(url.groups?.url || "");
|
|
20
20
|
if (source) {
|
|
@@ -25,9 +25,9 @@ function* parseFontFace(css) {
|
|
|
25
25
|
}
|
|
26
26
|
yield { family: "", source: "" };
|
|
27
27
|
}
|
|
28
|
-
const
|
|
28
|
+
const generateFallbackName = (name) => {
|
|
29
29
|
const firstFamily = withoutQuotes(name.split(",").shift());
|
|
30
|
-
return `${firstFamily}
|
|
30
|
+
return `${firstFamily} fallback`;
|
|
31
31
|
};
|
|
32
32
|
const withoutQuotes = (str) => str.trim().replace(QUOTES_RE, "");
|
|
33
33
|
const generateFontFace = (metrics, options) => {
|
|
@@ -36,6 +36,7 @@ const generateFontFace = (metrics, options) => {
|
|
|
36
36
|
const declaration = {
|
|
37
37
|
"font-family": JSON.stringify(name),
|
|
38
38
|
src: fallbacks.map((f) => `local(${JSON.stringify(f)})`),
|
|
39
|
+
// 'size-adjust': toPercentage(sizeAdjust),
|
|
39
40
|
"ascent-override": toPercentage(
|
|
40
41
|
metrics.ascent / (metrics.unitsPerEm * sizeAdjust)
|
|
41
42
|
),
|
|
@@ -74,17 +75,28 @@ const URL_RE = magicRegexp.createRegExp(
|
|
|
74
75
|
);
|
|
75
76
|
|
|
76
77
|
const metricCache = {};
|
|
78
|
+
const filterRequiredMetrics = ({
|
|
79
|
+
ascent,
|
|
80
|
+
descent,
|
|
81
|
+
lineGap,
|
|
82
|
+
unitsPerEm
|
|
83
|
+
}) => ({
|
|
84
|
+
ascent,
|
|
85
|
+
descent,
|
|
86
|
+
lineGap,
|
|
87
|
+
unitsPerEm
|
|
88
|
+
});
|
|
77
89
|
async function getMetricsForFamily(family) {
|
|
78
90
|
family = withoutQuotes(family);
|
|
79
91
|
if (family in metricCache)
|
|
80
92
|
return metricCache[family];
|
|
81
93
|
try {
|
|
82
|
-
const name =
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
);
|
|
86
|
-
metricCache[family] =
|
|
87
|
-
return
|
|
94
|
+
const name = metrics.fontFamilyToCamelCase(family);
|
|
95
|
+
const { entireMetricsCollection } = await import('@capsizecss/metrics/entireMetricsCollection');
|
|
96
|
+
const metrics$1 = entireMetricsCollection[name];
|
|
97
|
+
const filteredMetrics = filterRequiredMetrics(metrics$1);
|
|
98
|
+
metricCache[family] = filteredMetrics;
|
|
99
|
+
return filteredMetrics;
|
|
88
100
|
} catch {
|
|
89
101
|
metricCache[family] = null;
|
|
90
102
|
return null;
|
|
@@ -99,8 +111,9 @@ async function readMetrics(_source) {
|
|
|
99
111
|
if (!protocol)
|
|
100
112
|
return null;
|
|
101
113
|
const metrics = protocol === "file:" ? await unpack.fromFile(node_url.fileURLToPath(source)) : await unpack.fromUrl(source);
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
const filteredMetrics = filterRequiredMetrics(metrics);
|
|
115
|
+
metricCache[source] = filteredMetrics;
|
|
116
|
+
return filteredMetrics;
|
|
104
117
|
}
|
|
105
118
|
|
|
106
119
|
const supportedExtensions = ["woff2", "woff", "ttf"];
|
|
@@ -109,7 +122,7 @@ const FontaineTransform = unplugin.createUnplugin(
|
|
|
109
122
|
const cssContext = options.css = options.css || {};
|
|
110
123
|
cssContext.value = "";
|
|
111
124
|
const resolvePath = options.resolvePath || ((id) => id);
|
|
112
|
-
const
|
|
125
|
+
const fallbackName = options.fallbackName || options.overrideName || generateFallbackName;
|
|
113
126
|
function readMetricsFromId(path, importer) {
|
|
114
127
|
const resolvedPath = pathe.isAbsolute(importer) && path.startsWith(".") ? pathe.join(importer, path) : resolvePath(path);
|
|
115
128
|
return readMetrics(resolvedPath);
|
|
@@ -137,7 +150,7 @@ const FontaineTransform = unplugin.createUnplugin(
|
|
|
137
150
|
const metrics = await getMetricsForFamily(family) || source && await readMetricsFromId(source, id).catch(() => null);
|
|
138
151
|
if (metrics) {
|
|
139
152
|
const fontFace = generateFontFace(metrics, {
|
|
140
|
-
name:
|
|
153
|
+
name: fallbackName(family),
|
|
141
154
|
fallbacks: options.fallbacks
|
|
142
155
|
});
|
|
143
156
|
cssContext.value += fontFace;
|
|
@@ -152,14 +165,14 @@ const FontaineTransform = unplugin.createUnplugin(
|
|
|
152
165
|
if (faceRanges.some(([start, end]) => index > start && index < end))
|
|
153
166
|
continue;
|
|
154
167
|
const families = matchContent.split(",").map((f) => f.trim()).filter((f) => !f.startsWith("var("));
|
|
155
|
-
if (!families.length)
|
|
168
|
+
if (!families.length || families[0] === "inherit")
|
|
156
169
|
continue;
|
|
157
170
|
s.overwrite(
|
|
158
171
|
index,
|
|
159
172
|
index + matchContent.length,
|
|
160
173
|
" " + [
|
|
161
174
|
families[0],
|
|
162
|
-
`"${
|
|
175
|
+
`"${generateFallbackName(families[0])}"`,
|
|
163
176
|
...families.slice(1)
|
|
164
177
|
].join(", ")
|
|
165
178
|
);
|
|
@@ -187,7 +200,7 @@ const CSS_RE = magicRegexp.createRegExp(
|
|
|
187
200
|
);
|
|
188
201
|
|
|
189
202
|
exports.FontaineTransform = FontaineTransform;
|
|
203
|
+
exports.generateFallbackName = generateFallbackName;
|
|
190
204
|
exports.generateFontFace = generateFontFace;
|
|
191
|
-
exports.generateOverrideName = generateOverrideName;
|
|
192
205
|
exports.getMetricsForFamily = getMetricsForFamily;
|
|
193
206
|
exports.readMetrics = readMetrics;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,42 +8,23 @@ interface FontaineTransformOptions {
|
|
|
8
8
|
fallbacks: string[];
|
|
9
9
|
resolvePath?: (path: string) => string | URL;
|
|
10
10
|
/** this should produce an unquoted font family name */
|
|
11
|
+
fallbackName?: (name: string) => string;
|
|
12
|
+
/** @deprecated use fallbackName */
|
|
11
13
|
overrideName?: (name: string) => string;
|
|
12
14
|
sourcemap?: boolean;
|
|
13
15
|
}
|
|
14
|
-
declare const FontaineTransform: unplugin.UnpluginInstance<FontaineTransformOptions,
|
|
16
|
+
declare const FontaineTransform: unplugin.UnpluginInstance<FontaineTransformOptions, boolean>;
|
|
15
17
|
|
|
16
|
-
declare const
|
|
18
|
+
declare const generateFallbackName: (name: string) => string;
|
|
17
19
|
interface GenerateOptions {
|
|
18
20
|
name: string;
|
|
19
21
|
fallbacks: string[];
|
|
20
22
|
[key: string]: any;
|
|
21
23
|
}
|
|
22
|
-
|
|
24
|
+
type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm'>;
|
|
25
|
+
declare const generateFontFace: (metrics: FontFaceMetrics, options: GenerateOptions) => string;
|
|
23
26
|
|
|
24
|
-
declare function getMetricsForFamily(family: string): Promise<
|
|
25
|
-
|
|
26
|
-
fullName: string;
|
|
27
|
-
postscriptName: string;
|
|
28
|
-
subfamilyName: string;
|
|
29
|
-
capHeight: number;
|
|
30
|
-
ascent: number;
|
|
31
|
-
descent: number;
|
|
32
|
-
lineGap: number;
|
|
33
|
-
unitsPerEm: number;
|
|
34
|
-
xHeight: number;
|
|
35
|
-
} | null>;
|
|
36
|
-
declare function readMetrics(_source: URL | string): Promise<{
|
|
37
|
-
familyName: string;
|
|
38
|
-
fullName: string;
|
|
39
|
-
postscriptName: string;
|
|
40
|
-
subfamilyName: string;
|
|
41
|
-
capHeight: number;
|
|
42
|
-
ascent: number;
|
|
43
|
-
descent: number;
|
|
44
|
-
lineGap: number;
|
|
45
|
-
unitsPerEm: number;
|
|
46
|
-
xHeight: number;
|
|
47
|
-
} | null>;
|
|
27
|
+
declare function getMetricsForFamily(family: string): Promise<FontFaceMetrics | null>;
|
|
28
|
+
declare function readMetrics(_source: URL | string): Promise<FontFaceMetrics | null>;
|
|
48
29
|
|
|
49
|
-
export { FontaineTransform,
|
|
30
|
+
export { FontaineTransform, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
|
package/dist/index.mjs
CHANGED
|
@@ -3,16 +3,16 @@ import { createRegExp, charIn, exactly, whitespace, charNotIn, anyOf } from 'mag
|
|
|
3
3
|
import MagicString from 'magic-string';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { fromFile, fromUrl } from '@capsizecss/unpack';
|
|
6
|
+
import { fontFamilyToCamelCase } from '@capsizecss/metrics';
|
|
6
7
|
import { parseURL } from 'ufo';
|
|
7
|
-
import { camelCase } from 'scule';
|
|
8
8
|
import { isAbsolute, join } from 'pathe';
|
|
9
9
|
|
|
10
10
|
function* parseFontFace(css) {
|
|
11
11
|
const fontFamily = css.match(FAMILY_RE)?.groups.fontFamily;
|
|
12
12
|
const family = withoutQuotes(fontFamily?.split(",")[0] || "");
|
|
13
13
|
for (const match of css.matchAll(SOURCE_RE)) {
|
|
14
|
-
const sources = match.groups.src?.split(",")
|
|
15
|
-
for (const entry of sources) {
|
|
14
|
+
const sources = match.groups.src?.split(",");
|
|
15
|
+
for (const entry of sources || []) {
|
|
16
16
|
for (const url of entry.matchAll(URL_RE)) {
|
|
17
17
|
const source = withoutQuotes(url.groups?.url || "");
|
|
18
18
|
if (source) {
|
|
@@ -23,9 +23,9 @@ function* parseFontFace(css) {
|
|
|
23
23
|
}
|
|
24
24
|
yield { family: "", source: "" };
|
|
25
25
|
}
|
|
26
|
-
const
|
|
26
|
+
const generateFallbackName = (name) => {
|
|
27
27
|
const firstFamily = withoutQuotes(name.split(",").shift());
|
|
28
|
-
return `${firstFamily}
|
|
28
|
+
return `${firstFamily} fallback`;
|
|
29
29
|
};
|
|
30
30
|
const withoutQuotes = (str) => str.trim().replace(QUOTES_RE, "");
|
|
31
31
|
const generateFontFace = (metrics, options) => {
|
|
@@ -34,6 +34,7 @@ const generateFontFace = (metrics, options) => {
|
|
|
34
34
|
const declaration = {
|
|
35
35
|
"font-family": JSON.stringify(name),
|
|
36
36
|
src: fallbacks.map((f) => `local(${JSON.stringify(f)})`),
|
|
37
|
+
// 'size-adjust': toPercentage(sizeAdjust),
|
|
37
38
|
"ascent-override": toPercentage(
|
|
38
39
|
metrics.ascent / (metrics.unitsPerEm * sizeAdjust)
|
|
39
40
|
),
|
|
@@ -72,17 +73,28 @@ const URL_RE = createRegExp(
|
|
|
72
73
|
);
|
|
73
74
|
|
|
74
75
|
const metricCache = {};
|
|
76
|
+
const filterRequiredMetrics = ({
|
|
77
|
+
ascent,
|
|
78
|
+
descent,
|
|
79
|
+
lineGap,
|
|
80
|
+
unitsPerEm
|
|
81
|
+
}) => ({
|
|
82
|
+
ascent,
|
|
83
|
+
descent,
|
|
84
|
+
lineGap,
|
|
85
|
+
unitsPerEm
|
|
86
|
+
});
|
|
75
87
|
async function getMetricsForFamily(family) {
|
|
76
88
|
family = withoutQuotes(family);
|
|
77
89
|
if (family in metricCache)
|
|
78
90
|
return metricCache[family];
|
|
79
91
|
try {
|
|
80
|
-
const name =
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
);
|
|
84
|
-
metricCache[family] =
|
|
85
|
-
return
|
|
92
|
+
const name = fontFamilyToCamelCase(family);
|
|
93
|
+
const { entireMetricsCollection } = await import('@capsizecss/metrics/entireMetricsCollection');
|
|
94
|
+
const metrics = entireMetricsCollection[name];
|
|
95
|
+
const filteredMetrics = filterRequiredMetrics(metrics);
|
|
96
|
+
metricCache[family] = filteredMetrics;
|
|
97
|
+
return filteredMetrics;
|
|
86
98
|
} catch {
|
|
87
99
|
metricCache[family] = null;
|
|
88
100
|
return null;
|
|
@@ -97,8 +109,9 @@ async function readMetrics(_source) {
|
|
|
97
109
|
if (!protocol)
|
|
98
110
|
return null;
|
|
99
111
|
const metrics = protocol === "file:" ? await fromFile(fileURLToPath(source)) : await fromUrl(source);
|
|
100
|
-
|
|
101
|
-
|
|
112
|
+
const filteredMetrics = filterRequiredMetrics(metrics);
|
|
113
|
+
metricCache[source] = filteredMetrics;
|
|
114
|
+
return filteredMetrics;
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
const supportedExtensions = ["woff2", "woff", "ttf"];
|
|
@@ -107,7 +120,7 @@ const FontaineTransform = createUnplugin(
|
|
|
107
120
|
const cssContext = options.css = options.css || {};
|
|
108
121
|
cssContext.value = "";
|
|
109
122
|
const resolvePath = options.resolvePath || ((id) => id);
|
|
110
|
-
const
|
|
123
|
+
const fallbackName = options.fallbackName || options.overrideName || generateFallbackName;
|
|
111
124
|
function readMetricsFromId(path, importer) {
|
|
112
125
|
const resolvedPath = isAbsolute(importer) && path.startsWith(".") ? join(importer, path) : resolvePath(path);
|
|
113
126
|
return readMetrics(resolvedPath);
|
|
@@ -135,7 +148,7 @@ const FontaineTransform = createUnplugin(
|
|
|
135
148
|
const metrics = await getMetricsForFamily(family) || source && await readMetricsFromId(source, id).catch(() => null);
|
|
136
149
|
if (metrics) {
|
|
137
150
|
const fontFace = generateFontFace(metrics, {
|
|
138
|
-
name:
|
|
151
|
+
name: fallbackName(family),
|
|
139
152
|
fallbacks: options.fallbacks
|
|
140
153
|
});
|
|
141
154
|
cssContext.value += fontFace;
|
|
@@ -150,14 +163,14 @@ const FontaineTransform = createUnplugin(
|
|
|
150
163
|
if (faceRanges.some(([start, end]) => index > start && index < end))
|
|
151
164
|
continue;
|
|
152
165
|
const families = matchContent.split(",").map((f) => f.trim()).filter((f) => !f.startsWith("var("));
|
|
153
|
-
if (!families.length)
|
|
166
|
+
if (!families.length || families[0] === "inherit")
|
|
154
167
|
continue;
|
|
155
168
|
s.overwrite(
|
|
156
169
|
index,
|
|
157
170
|
index + matchContent.length,
|
|
158
171
|
" " + [
|
|
159
172
|
families[0],
|
|
160
|
-
`"${
|
|
173
|
+
`"${generateFallbackName(families[0])}"`,
|
|
161
174
|
...families.slice(1)
|
|
162
175
|
].join(", ")
|
|
163
176
|
);
|
|
@@ -184,4 +197,4 @@ const CSS_RE = createRegExp(
|
|
|
184
197
|
exactly(".").and(anyOf("sass", "css", "scss")).at.lineEnd()
|
|
185
198
|
);
|
|
186
199
|
|
|
187
|
-
export { FontaineTransform,
|
|
200
|
+
export { FontaineTransform, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fontaine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Automatic font fallback based on font metrics",
|
|
5
5
|
"repository": "unjs/fontaine",
|
|
6
6
|
"keywords": [
|
|
@@ -40,48 +40,45 @@
|
|
|
40
40
|
"lint:prettier": "prettier --write --loglevel warn",
|
|
41
41
|
"prepare": "husky install && pnpm build",
|
|
42
42
|
"prepublishOnly": "pnpm lint && pnpm test && pinst --disable",
|
|
43
|
-
"release": "
|
|
43
|
+
"release": "pnpm test && bumpp && npm publish",
|
|
44
44
|
"test": "vitest run",
|
|
45
45
|
"_postinstall": "husky install",
|
|
46
46
|
"postpublish": "pinst --enable"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@capsizecss/metrics": "^
|
|
50
|
-
"@capsizecss/unpack": "^
|
|
51
|
-
"magic-regexp": "^0.6.
|
|
52
|
-
"magic-string": "^0.
|
|
53
|
-
"pathe": "^
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"unplugin": "^0.10.2"
|
|
49
|
+
"@capsizecss/metrics": "^1.1.1",
|
|
50
|
+
"@capsizecss/unpack": "^1.0.0",
|
|
51
|
+
"magic-regexp": "^0.6.3",
|
|
52
|
+
"magic-string": "^0.30.0",
|
|
53
|
+
"pathe": "^1.1.0",
|
|
54
|
+
"ufo": "^1.1.1",
|
|
55
|
+
"unplugin": "^1.1.0"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
58
|
"@nuxtjs/eslint-config-typescript": "latest",
|
|
60
|
-
"@
|
|
61
|
-
"@types/
|
|
62
|
-
"@
|
|
63
|
-
"@typescript-eslint/
|
|
64
|
-
"@
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"eslint": "^8.26.0",
|
|
59
|
+
"@types/node": "18.14.6",
|
|
60
|
+
"@types/serve-handler": "6.1.1",
|
|
61
|
+
"@typescript-eslint/eslint-plugin": "5.54.0",
|
|
62
|
+
"@typescript-eslint/parser": "5.54.0",
|
|
63
|
+
"@vitest/coverage-c8": "0.29.2",
|
|
64
|
+
"bumpp": "^9.0.0",
|
|
65
|
+
"eslint": "8.35.0",
|
|
68
66
|
"eslint-config-prettier": "latest",
|
|
69
67
|
"eslint-plugin-prettier": "latest",
|
|
70
|
-
"execa": "
|
|
71
|
-
"get-port-please": "
|
|
68
|
+
"execa": "7.0.0",
|
|
69
|
+
"get-port-please": "3.0.1",
|
|
72
70
|
"husky": "latest",
|
|
73
71
|
"lint-staged": "latest",
|
|
74
72
|
"pinst": "latest",
|
|
75
73
|
"prettier": "latest",
|
|
76
|
-
"
|
|
77
|
-
"serve-handler": "^6.1.3",
|
|
74
|
+
"serve-handler": "6.1.5",
|
|
78
75
|
"typescript": "latest",
|
|
79
76
|
"unbuild": "latest",
|
|
80
|
-
"vite": "
|
|
81
|
-
"vitest": "
|
|
77
|
+
"vite": "4.1.4",
|
|
78
|
+
"vitest": "0.29.2"
|
|
82
79
|
},
|
|
83
80
|
"resolutions": {
|
|
84
81
|
"fontaine": "link:."
|
|
85
82
|
},
|
|
86
|
-
"packageManager": "pnpm@7.
|
|
83
|
+
"packageManager": "pnpm@7.28.0"
|
|
87
84
|
}
|