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 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 generateOverrideName = (name) => {
28
+ const generateFallbackName = (name) => {
29
29
  const firstFamily = withoutQuotes(name.split(",").shift());
30
- return `${firstFamily} override`;
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 = scule.camelCase(family).replace(/ /, "");
83
- const metrics = await import(`@capsizecss/metrics/${name}.js`).then(
84
- (r) => r.default || r
85
- );
86
- metricCache[family] = metrics;
87
- return metrics;
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
- metricCache[source] = metrics;
103
- return metrics;
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 overrideName = options.overrideName || generateOverrideName;
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: overrideName(family),
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
- `"${generateOverrideName(families[0])}"`,
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, false>;
16
+ declare const FontaineTransform: unplugin.UnpluginInstance<FontaineTransformOptions, boolean>;
15
17
 
16
- declare const generateOverrideName: (name: string) => string;
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
- declare const generateFontFace: (metrics: Font, options: GenerateOptions) => string;
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
- familyName: string;
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, generateFontFace, generateOverrideName, getMetricsForFamily, readMetrics };
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 generateOverrideName = (name) => {
26
+ const generateFallbackName = (name) => {
27
27
  const firstFamily = withoutQuotes(name.split(",").shift());
28
- return `${firstFamily} override`;
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 = camelCase(family).replace(/ /, "");
81
- const metrics = await import(`@capsizecss/metrics/${name}.js`).then(
82
- (r) => r.default || r
83
- );
84
- metricCache[family] = metrics;
85
- return metrics;
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
- metricCache[source] = metrics;
101
- return metrics;
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 overrideName = options.overrideName || generateOverrideName;
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: overrideName(family),
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
- `"${generateOverrideName(families[0])}"`,
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, generateFontFace, generateOverrideName, getMetricsForFamily, readMetrics };
200
+ export { FontaineTransform, generateFallbackName, generateFontFace, getMetricsForFamily, readMetrics };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fontaine",
3
- "version": "0.2.3",
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": "release-it",
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": "^0.3.0",
50
- "@capsizecss/unpack": "^0.1.0",
51
- "magic-regexp": "^0.6.0",
52
- "magic-string": "^0.26.7",
53
- "pathe": "^0.3.9",
54
- "scule": "^0.3.2",
55
- "ufo": "^0.8.6",
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
- "@release-it/conventional-changelog": "latest",
61
- "@types/node": "^18.11.8",
62
- "@types/serve-handler": "^6.1.1",
63
- "@typescript-eslint/eslint-plugin": "^5.41.0",
64
- "@typescript-eslint/parser": "^5.41.0",
65
- "@vitest/coverage-c8": "^0.24.4",
66
- "conventional-changelog-conventionalcommits": "latest",
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": "^6.1.0",
71
- "get-port-please": "^2.6.1",
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
- "release-it": "latest",
77
- "serve-handler": "^6.1.3",
74
+ "serve-handler": "6.1.5",
78
75
  "typescript": "latest",
79
76
  "unbuild": "latest",
80
- "vite": "^3.2.2",
81
- "vitest": "^0.24.4"
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.14.1"
83
+ "packageManager": "pnpm@7.28.0"
87
84
  }