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.
Files changed (64) hide show
  1. package/dist/assets/fonts/config.d.ts +3 -3
  2. package/dist/assets/fonts/config.js +2 -3
  3. package/dist/assets/fonts/constants.d.ts +3 -73
  4. package/dist/assets/fonts/constants.js +16 -68
  5. package/dist/assets/fonts/definitions.d.ts +73 -0
  6. package/dist/assets/fonts/definitions.js +0 -0
  7. package/dist/assets/fonts/implementations/css-renderer.d.ts +9 -0
  8. package/dist/assets/fonts/implementations/css-renderer.js +42 -0
  9. package/dist/assets/fonts/implementations/data-collector.d.ts +3 -0
  10. package/dist/assets/fonts/implementations/data-collector.js +21 -0
  11. package/dist/assets/fonts/implementations/error-handler.d.ts +2 -0
  12. package/dist/assets/fonts/implementations/error-handler.js +33 -0
  13. package/dist/assets/fonts/implementations/font-fetcher.d.ts +8 -0
  14. package/dist/assets/fonts/implementations/font-fetcher.js +34 -0
  15. package/dist/assets/fonts/implementations/font-metrics-resolver.d.ts +5 -0
  16. package/dist/assets/fonts/implementations/font-metrics-resolver.js +60 -0
  17. package/dist/assets/fonts/implementations/font-type-extractor.d.ts +4 -0
  18. package/dist/assets/fonts/implementations/font-type-extractor.js +22 -0
  19. package/dist/assets/fonts/implementations/hasher.d.ts +2 -0
  20. package/dist/assets/fonts/implementations/hasher.js +14 -0
  21. package/dist/assets/fonts/implementations/local-provider-url-resolver.d.ts +5 -0
  22. package/dist/assets/fonts/implementations/local-provider-url-resolver.js +17 -0
  23. package/dist/assets/fonts/implementations/remote-font-provider-mod-resolver.d.ts +6 -0
  24. package/dist/assets/fonts/implementations/remote-font-provider-mod-resolver.js +20 -0
  25. package/dist/assets/fonts/implementations/remote-font-provider-resolver.d.ts +6 -0
  26. package/dist/assets/fonts/implementations/remote-font-provider-resolver.js +47 -0
  27. package/dist/assets/fonts/implementations/storage.d.ts +4 -0
  28. package/dist/assets/fonts/implementations/storage.js +14 -0
  29. package/dist/assets/fonts/implementations/system-fallbacks-provider.d.ts +11 -0
  30. package/dist/assets/fonts/implementations/system-fallbacks-provider.js +74 -0
  31. package/dist/assets/fonts/implementations/url-proxy-content-resolver.d.ts +5 -0
  32. package/dist/assets/fonts/implementations/url-proxy-content-resolver.js +28 -0
  33. package/dist/assets/fonts/implementations/url-proxy.d.ts +8 -0
  34. package/dist/assets/fonts/implementations/url-proxy.js +26 -0
  35. package/dist/assets/fonts/logic/extract-unifont-providers.d.ts +10 -0
  36. package/dist/assets/fonts/logic/extract-unifont-providers.js +28 -0
  37. package/dist/assets/fonts/logic/normalize-remote-font-faces.d.ts +6 -0
  38. package/dist/assets/fonts/logic/normalize-remote-font-faces.js +36 -0
  39. package/dist/assets/fonts/logic/optimize-fallbacks.d.ts +17 -0
  40. package/dist/assets/fonts/logic/optimize-fallbacks.js +47 -0
  41. package/dist/assets/fonts/logic/resolve-families.d.ts +17 -0
  42. package/dist/assets/fonts/logic/resolve-families.js +67 -0
  43. package/dist/assets/fonts/orchestrate.d.ts +37 -0
  44. package/dist/assets/fonts/orchestrate.js +125 -0
  45. package/dist/assets/fonts/providers/local.d.ts +6 -7
  46. package/dist/assets/fonts/providers/local.js +26 -29
  47. package/dist/assets/fonts/types.d.ts +32 -2
  48. package/dist/assets/fonts/utils.d.ts +17 -88
  49. package/dist/assets/fonts/utils.js +20 -186
  50. package/dist/assets/fonts/vite-plugin-fonts.js +96 -94
  51. package/dist/content/content-layer.js +3 -3
  52. package/dist/core/constants.js +1 -1
  53. package/dist/core/dev/dev.js +1 -1
  54. package/dist/core/messages.js +2 -2
  55. package/dist/types/public/config.d.ts +2 -2
  56. package/dist/vite-plugin-markdown/images.js +4 -4
  57. package/package.json +2 -2
  58. package/types/content.d.ts +11 -4
  59. package/dist/assets/fonts/load.d.ts +0 -20
  60. package/dist/assets/fonts/load.js +0 -162
  61. package/dist/assets/fonts/metrics.d.ts +0 -10
  62. package/dist/assets/fonts/metrics.js +0 -55
  63. package/dist/assets/fonts/providers/utils.d.ts +0 -9
  64. package/dist/assets/fonts/providers/utils.js +0 -37
@@ -3,49 +3,38 @@ import { readFile } from "node:fs/promises";
3
3
  import { isAbsolute } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { removeTrailingForwardSlash } from "@astrojs/internal-helpers/path";
6
- import { createStorage } from "unstorage";
7
- import fsLiteDriver from "unstorage/drivers/fs-lite";
8
- import xxhash from "xxhash-wasm";
9
6
  import { collectErrorMetadata } from "../../core/errors/dev/utils.js";
10
7
  import { AstroError, AstroErrorData, isAstroError } from "../../core/errors/index.js";
11
8
  import { formatErrorMessage } from "../../core/messages.js";
12
9
  import { getClientOutputDirectory } from "../../prerender/utils.js";
13
10
  import {
14
11
  CACHE_DIR,
12
+ DEFAULTS,
15
13
  RESOLVED_VIRTUAL_MODULE_ID,
16
14
  URL_PREFIX,
17
15
  VIRTUAL_MODULE_ID
18
16
  } from "./constants.js";
19
- import { loadFonts } from "./load.js";
20
- import { generateFallbackFontFace, readMetrics } from "./metrics.js";
17
+ import { createMinifiableCssRenderer } from "./implementations/css-renderer.js";
18
+ import { createDataCollector } from "./implementations/data-collector.js";
19
+ import { createAstroErrorHandler } from "./implementations/error-handler.js";
20
+ import { createCachedFontFetcher } from "./implementations/font-fetcher.js";
21
+ import { createCapsizeFontMetricsResolver } from "./implementations/font-metrics-resolver.js";
22
+ import { createFontTypeExtractor } from "./implementations/font-type-extractor.js";
23
+ import { createXxHasher } from "./implementations/hasher.js";
24
+ import { createRequireLocalProviderUrlResolver } from "./implementations/local-provider-url-resolver.js";
21
25
  import {
22
- cache,
23
- extractFontType,
24
- resolveEntrypoint,
25
- resolveFontFamily,
26
- sortObjectByKey,
27
- withoutQuotes
28
- } from "./utils.js";
29
- async function fetchFont(url) {
30
- try {
31
- if (isAbsolute(url)) {
32
- return await readFile(url);
33
- }
34
- const response = await fetch(url);
35
- if (!response.ok) {
36
- throw new Error(`Response was not successful, received status code ${response.status}`);
37
- }
38
- return Buffer.from(await response.arrayBuffer());
39
- } catch (cause) {
40
- throw new AstroError(
41
- {
42
- ...AstroErrorData.CannotFetchFontFile,
43
- message: AstroErrorData.CannotFetchFontFile.message(url)
44
- },
45
- { cause }
46
- );
47
- }
48
- }
26
+ createBuildRemoteFontProviderModResolver,
27
+ createDevServerRemoteFontProviderModResolver
28
+ } from "./implementations/remote-font-provider-mod-resolver.js";
29
+ import { createRemoteFontProviderResolver } from "./implementations/remote-font-provider-resolver.js";
30
+ import { createFsStorage } from "./implementations/storage.js";
31
+ import { createSystemFallbacksProvider } from "./implementations/system-fallbacks-provider.js";
32
+ import {
33
+ createLocalUrlProxyContentResolver,
34
+ createRemoteUrlProxyContentResolver
35
+ } from "./implementations/url-proxy-content-resolver.js";
36
+ import { createUrlProxy } from "./implementations/url-proxy.js";
37
+ import { orchestrate } from "./orchestrate.js";
49
38
  function fontsPlugin({ settings, sync, logger }) {
50
39
  if (!settings.config.experimental.fonts) {
51
40
  return {
@@ -65,64 +54,75 @@ function fontsPlugin({ settings, sync, logger }) {
65
54
  };
66
55
  }
67
56
  const baseUrl = removeTrailingForwardSlash(settings.config.base) + URL_PREFIX;
68
- let resolvedMap = null;
69
- let hashToUrlMap = null;
57
+ let fontFileDataMap = null;
58
+ let consumableMap = null;
70
59
  let isBuild;
71
- let storage = null;
60
+ let fontFetcher = null;
61
+ let fontTypeExtractor = null;
72
62
  const cleanup = () => {
73
- resolvedMap = null;
74
- hashToUrlMap = null;
75
- storage = null;
63
+ consumableMap = null;
64
+ fontFileDataMap = null;
65
+ fontFetcher = null;
76
66
  };
77
- async function initialize({ resolveMod, base }) {
78
- const { h64ToString } = await xxhash();
79
- storage = createStorage({
80
- // Types are weirly exported
81
- driver: fsLiteDriver({
82
- base: fileURLToPath(base)
83
- })
67
+ async function initialize({
68
+ cacheDir,
69
+ modResolver,
70
+ cssRenderer
71
+ }) {
72
+ const { root } = settings.config;
73
+ const hasher = await createXxHasher();
74
+ const errorHandler = createAstroErrorHandler();
75
+ const remoteFontProviderResolver = createRemoteFontProviderResolver({
76
+ root,
77
+ modResolver,
78
+ errorHandler
84
79
  });
85
- hashToUrlMap = /* @__PURE__ */ new Map();
86
- resolvedMap = /* @__PURE__ */ new Map();
87
- const families = [];
88
- const root = settings.config.root;
89
80
  const pathsToWarn = /* @__PURE__ */ new Set();
90
- for (const family of settings.config.experimental.fonts) {
91
- families.push(
92
- await resolveFontFamily({
93
- family,
94
- root,
95
- resolveMod,
96
- generateNameWithHash: (_family) => `${withoutQuotes(_family.name)}-${h64ToString(JSON.stringify(sortObjectByKey(_family)))}`,
97
- resolveLocalEntrypoint: (url) => {
98
- const resolvedPath = fileURLToPath(resolveEntrypoint(root, url));
99
- if (resolvedPath.startsWith(fileURLToPath(settings.config.publicDir))) {
100
- pathsToWarn.add(resolvedPath);
101
- }
102
- return resolvedPath;
81
+ const localProviderUrlResolver = createRequireLocalProviderUrlResolver({
82
+ root,
83
+ intercept: (path) => {
84
+ if (path.startsWith(fileURLToPath(settings.config.publicDir))) {
85
+ if (pathsToWarn.has(path)) {
86
+ return;
103
87
  }
104
- })
105
- );
106
- }
107
- for (const path of [...pathsToWarn]) {
108
- logger.warn(
109
- "assets",
110
- `Found a local font file ${JSON.stringify(path)} in the \`public/\` folder. To avoid duplicated files in the build output, move this file into \`src/\``
111
- );
112
- }
113
- await loadFonts({
114
- base: baseUrl,
115
- families,
88
+ pathsToWarn.add(path);
89
+ logger.warn(
90
+ "assets",
91
+ `Found a local font file ${JSON.stringify(path)} in the \`public/\` folder. To avoid duplicated files in the build output, move this file into \`src/\``
92
+ );
93
+ }
94
+ }
95
+ });
96
+ const storage = createFsStorage({ base: cacheDir });
97
+ const systemFallbacksProvider = createSystemFallbacksProvider();
98
+ fontFetcher = createCachedFontFetcher({ storage, errorHandler, fetch, readFile });
99
+ const fontMetricsResolver = createCapsizeFontMetricsResolver({ fontFetcher, cssRenderer });
100
+ fontTypeExtractor = createFontTypeExtractor({ errorHandler });
101
+ const res = await orchestrate({
102
+ families: settings.config.experimental.fonts,
103
+ hasher,
104
+ remoteFontProviderResolver,
105
+ localProviderUrlResolver,
116
106
  storage,
117
- hashToUrlMap,
118
- resolvedMap,
119
- hashString: h64ToString,
120
- generateFallbackFontFace,
121
- getMetricsForFamily: async (name, font) => {
122
- return await readMetrics(name, await cache(storage, font.hash, () => fetchFont(font.url)));
107
+ cssRenderer,
108
+ systemFallbacksProvider,
109
+ fontMetricsResolver,
110
+ fontTypeExtractor,
111
+ createUrlProxy: ({ local, ...params }) => {
112
+ const dataCollector = createDataCollector(params);
113
+ const contentResolver = local ? createLocalUrlProxyContentResolver({ errorHandler }) : createRemoteUrlProxyContentResolver();
114
+ return createUrlProxy({
115
+ base: baseUrl,
116
+ contentResolver,
117
+ hasher,
118
+ dataCollector,
119
+ fontTypeExtractor
120
+ });
123
121
  },
124
- log: (message) => logger.info("assets", message)
122
+ defaults: DEFAULTS
125
123
  });
124
+ fontFileDataMap = res.fontFileDataMap;
125
+ consumableMap = res.consumableMap;
126
126
  }
127
127
  return {
128
128
  name: "astro:fonts",
@@ -132,18 +132,20 @@ function fontsPlugin({ settings, sync, logger }) {
132
132
  async buildStart() {
133
133
  if (isBuild) {
134
134
  await initialize({
135
- resolveMod: (id) => import(id),
136
- base: new URL(CACHE_DIR, settings.config.cacheDir)
135
+ cacheDir: new URL(CACHE_DIR, settings.config.cacheDir),
136
+ modResolver: createBuildRemoteFontProviderModResolver(),
137
+ cssRenderer: createMinifiableCssRenderer({ minify: true })
137
138
  });
138
139
  }
139
140
  },
140
141
  async configureServer(server) {
141
142
  await initialize({
142
- resolveMod: (id) => server.ssrLoadModule(id),
143
143
  // In dev, we cache fonts data in .astro so it can be easily inspected and cleared
144
- base: new URL(CACHE_DIR, settings.dotAstroDir)
144
+ cacheDir: new URL(CACHE_DIR, settings.dotAstroDir),
145
+ modResolver: createDevServerRemoteFontProviderModResolver({ server }),
146
+ cssRenderer: createMinifiableCssRenderer({ minify: false })
145
147
  });
146
- const localPaths = [...hashToUrlMap.values()].filter((url) => isAbsolute(url));
148
+ const localPaths = [...fontFileDataMap.values()].filter(({ url }) => isAbsolute(url)).map((v) => v.url);
147
149
  server.watcher.on("change", (path) => {
148
150
  if (localPaths.includes(path)) {
149
151
  logger.info("assets", "Font file updated");
@@ -163,17 +165,17 @@ function fontsPlugin({ settings, sync, logger }) {
163
165
  return next();
164
166
  }
165
167
  const hash = req.url.slice(1);
166
- const url = hashToUrlMap?.get(hash);
167
- if (!url) {
168
+ const associatedData = fontFileDataMap?.get(hash);
169
+ if (!associatedData) {
168
170
  return next();
169
171
  }
170
172
  res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
171
173
  res.setHeader("Pragma", "no-cache");
172
174
  res.setHeader("Expires", 0);
173
175
  try {
174
- const data = await cache(storage, hash, () => fetchFont(url));
176
+ const data = await fontFetcher.fetch({ hash, ...associatedData });
175
177
  res.setHeader("Content-Length", data.length);
176
- res.setHeader("Content-Type", `font/${extractFontType(hash)}`);
178
+ res.setHeader("Content-Type", `font/${fontTypeExtractor.extract(hash)}`);
177
179
  res.end(data);
178
180
  } catch (err) {
179
181
  logger.error("assets", "Cannot download font file");
@@ -196,7 +198,7 @@ function fontsPlugin({ settings, sync, logger }) {
196
198
  load(id) {
197
199
  if (id === RESOLVED_VIRTUAL_MODULE_ID) {
198
200
  return {
199
- code: `export const fontsData = new Map(${JSON.stringify(Array.from(resolvedMap?.entries() ?? []))})`
201
+ code: `export const fontsData = new Map(${JSON.stringify(Array.from(consumableMap?.entries() ?? []))})`
200
202
  };
201
203
  }
202
204
  },
@@ -213,11 +215,11 @@ function fontsPlugin({ settings, sync, logger }) {
213
215
  } catch (cause) {
214
216
  throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause });
215
217
  }
216
- if (hashToUrlMap) {
218
+ if (fontFileDataMap) {
217
219
  logger.info("assets", "Copying fonts...");
218
220
  await Promise.all(
219
- Array.from(hashToUrlMap.entries()).map(async ([hash, url]) => {
220
- const data = await cache(storage, hash, () => fetchFont(url));
221
+ Array.from(fontFileDataMap.entries()).map(async ([hash, associatedData]) => {
222
+ const data = await fontFetcher.fetch({ hash, ...associatedData });
221
223
  try {
222
224
  writeFileSync(new URL(hash, fontsDir), data);
223
225
  } catch (cause) {
@@ -153,7 +153,7 @@ ${contentConfig.error.message}`);
153
153
  logger.info("Content config changed");
154
154
  shouldClear = true;
155
155
  }
156
- if (previousAstroVersion && previousAstroVersion !== "5.7.6") {
156
+ if (previousAstroVersion && previousAstroVersion !== "5.7.8") {
157
157
  logger.info("Astro version changed");
158
158
  shouldClear = true;
159
159
  }
@@ -161,8 +161,8 @@ ${contentConfig.error.message}`);
161
161
  logger.info("Clearing content store");
162
162
  this.#store.clearAll();
163
163
  }
164
- if ("5.7.6") {
165
- await this.#store.metaStore().set("astro-version", "5.7.6");
164
+ if ("5.7.8") {
165
+ await this.#store.metaStore().set("astro-version", "5.7.8");
166
166
  }
167
167
  if (currentConfigDigest) {
168
168
  await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "5.7.6";
1
+ const ASTRO_VERSION = "5.7.8";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
4
4
  const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
@@ -22,7 +22,7 @@ async function dev(inlineConfig) {
22
22
  await telemetry.record([]);
23
23
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
24
24
  const logger = restart.container.logger;
25
- const currentVersion = "5.7.6";
25
+ const currentVersion = "5.7.8";
26
26
  const isPrerelease = currentVersion.includes("-");
27
27
  if (!isPrerelease) {
28
28
  try {
@@ -37,7 +37,7 @@ function serverStart({
37
37
  host,
38
38
  base
39
39
  }) {
40
- const version = "5.7.6";
40
+ const version = "5.7.8";
41
41
  const localPrefix = `${dim("\u2503")} Local `;
42
42
  const networkPrefix = `${dim("\u2503")} Network `;
43
43
  const emptyPrefix = " ".repeat(11);
@@ -274,7 +274,7 @@ function printHelp({
274
274
  message.push(
275
275
  linebreak(),
276
276
  ` ${bgGreen(black(` ${commandName} `))} ${green(
277
- `v${"5.7.6"}`
277
+ `v${"5.7.8"}`
278
278
  )} ${headline}`
279
279
  );
280
280
  }
@@ -312,9 +312,9 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
312
312
  * @see output
313
313
  * @description
314
314
  *
315
- * Deploy to your favorite server, serverless, or edge host with build adapters. Import one of our first-party adapters for [Netlify](https://docs.astro.build/en/guides/deploy/netlify/#adapter-for-ssr), [Vercel](https://docs.astro.build/en/guides/deploy/vercel/#adapter-for-ssr), and more to engage Astro SSR.
315
+ * Deploy to your favorite server, serverless, or edge host with build adapters. Import one of our first-party adapters ([Cloudflare](/en/guides/integrations-guide/cloudflare/), [Netlify](/en/guides/integrations-guide/netlify/), [Node.js](/en/guides/integrations-guide/node/), [Vercel](/en/guides/integrations-guide/vercel/)) or explore [community adapters](https://astro.build/integrations/2/?search=&categories%5B%5D=adapters) to enable on-demand rendering in your Astro project.
316
316
  *
317
- * [See our On-demand Rendering guide](https://docs.astro.build/en/guides/on-demand-rendering/) for more on SSR, and [our deployment guides](https://docs.astro.build/en/guides/deploy/) for a complete list of hosts.
317
+ * See our [on-demand rendering guide](/en/guides/on-demand-rendering/) for more on Astro's server rendering options.
318
318
  *
319
319
  * ```js
320
320
  * import netlify from '@astrojs/netlify';
@@ -6,7 +6,7 @@ function getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html) {
6
6
  const images = async function(html) {
7
7
  const imageSources = {};
8
8
  ${localImagePaths.map((entry) => {
9
- const rawUrl = JSON.stringify(entry.raw);
9
+ const rawUrl = JSON.stringify(entry.raw).replace(/'/g, "'");
10
10
  return `{
11
11
  const regex = new RegExp('__ASTRO_IMAGE_="([^"]*' + ${rawUrl.replace(
12
12
  /[.*+?^${}()|[\]\\]/g,
@@ -16,7 +16,7 @@ function getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html) {
16
16
  let occurrenceCounter = 0;
17
17
  while ((match = regex.exec(html)) !== null) {
18
18
  const matchKey = ${rawUrl} + '_' + occurrenceCounter;
19
- const imageProps = JSON.parse(match[1].replace(/"/g, '"'));
19
+ const imageProps = JSON.parse(match[1].replace(/"/g, '"').replace(/'/g, "'"));
20
20
  const { src, ...props } = imageProps;
21
21
  imageSources[matchKey] = await getImage({src: Astro__${entry.safeName}, ...props});
22
22
  occurrenceCounter++;
@@ -24,7 +24,7 @@ function getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html) {
24
24
  }`;
25
25
  }).join("\n")}
26
26
  ${remoteImagePaths.map((raw) => {
27
- const rawUrl = JSON.stringify(raw);
27
+ const rawUrl = JSON.stringify(raw).replace(/'/g, "'");
28
28
  return `{
29
29
  const regex = new RegExp('__ASTRO_IMAGE_="([^"]*' + ${rawUrl.replace(
30
30
  /[.*+?^${}()|[\]\\]/g,
@@ -34,7 +34,7 @@ function getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html) {
34
34
  let occurrenceCounter = 0;
35
35
  while ((match = regex.exec(html)) !== null) {
36
36
  const matchKey = ${rawUrl} + '_' + occurrenceCounter;
37
- const props = JSON.parse(match[1].replace(/"/g, '"'));
37
+ const props = JSON.parse(match[1].replace(/"/g, '"').replace(/'/g, "'"));
38
38
  imageSources[matchKey] = await getImage(props);
39
39
  occurrenceCounter++;
40
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "5.7.6",
3
+ "version": "5.7.8",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -142,7 +142,7 @@
142
142
  "tinyglobby": "^0.2.12",
143
143
  "tsconfck": "^3.1.5",
144
144
  "ultrahtml": "^1.6.0",
145
- "unifont": "~0.2.0",
145
+ "unifont": "~0.4.1",
146
146
  "unist-util-visit": "^5.0.0",
147
147
  "unstorage": "^1.15.0",
148
148
  "vfile": "^6.0.3",
@@ -45,11 +45,18 @@ declare module 'astro:content' {
45
45
  has: (key: string) => boolean;
46
46
  }
47
47
 
48
+ type BaseAtomicSchema = import('astro/zod').AnyZodObject;
49
+
50
+ type BaseCompositeSchema =
51
+ | import('astro/zod').ZodUnion<[BaseAtomicSchema, ...BaseAtomicSchema[]]>
52
+ | import('astro/zod').ZodDiscriminatedUnion<string, BaseAtomicSchema[]>
53
+ // If we have a union of unions, give up on trying to type-check it all. You're on your own.
54
+ | import('astro/zod').ZodUnion<[import('astro/zod').ZodUnion<z.any>, ...z.any[]]>;
55
+
48
56
  type BaseSchemaWithoutEffects =
49
- | import('astro/zod').AnyZodObject
50
- | import('astro/zod').ZodUnion<[BaseSchemaWithoutEffects, ...BaseSchemaWithoutEffects[]]>
51
- | import('astro/zod').ZodDiscriminatedUnion<string, import('astro/zod').AnyZodObject[]>
52
- | import('astro/zod').ZodIntersection<BaseSchemaWithoutEffects, BaseSchemaWithoutEffects>;
57
+ | BaseAtomicSchema
58
+ | BaseCompositeSchema
59
+ | import('astro/zod').ZodIntersection<BaseAtomicSchema, BaseAtomicSchema | BaseCompositeSchema>;
53
60
 
54
61
  export type BaseSchema =
55
62
  | BaseSchemaWithoutEffects
@@ -1,20 +0,0 @@
1
- import type { Storage } from 'unstorage';
2
- import type { generateFallbackFontFace } from './metrics.js';
3
- import type { PreloadData, ResolvedFontFamily } from './types.js';
4
- import { type GetMetricsForFamily } from './utils.js';
5
- interface Options {
6
- base: string;
7
- families: Array<ResolvedFontFamily>;
8
- storage: Storage;
9
- hashToUrlMap: Map<string, string>;
10
- resolvedMap: Map<string, {
11
- preloadData: PreloadData;
12
- css: string;
13
- }>;
14
- hashString: (value: string) => string;
15
- log: (message: string) => void;
16
- generateFallbackFontFace: typeof generateFallbackFontFace;
17
- getMetricsForFamily: GetMetricsForFamily;
18
- }
19
- export declare function loadFonts({ base, families, storage, hashToUrlMap, resolvedMap, hashString, generateFallbackFontFace, getMetricsForFamily, log, }: Options): Promise<void>;
20
- export {};
@@ -1,162 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import * as unifont from "unifont";
3
- import { AstroError, AstroErrorData } from "../../core/errors/index.js";
4
- import { DEFAULTS, LOCAL_PROVIDER_NAME } from "./constants.js";
5
- import { resolveLocalFont } from "./providers/local.js";
6
- import {
7
- familiesToUnifontProviders,
8
- generateFallbacksCSS,
9
- generateFontFace,
10
- proxyURL
11
- } from "./utils.js";
12
- async function loadFonts({
13
- base,
14
- families,
15
- storage,
16
- hashToUrlMap,
17
- resolvedMap,
18
- hashString,
19
- generateFallbackFontFace,
20
- getMetricsForFamily,
21
- log
22
- }) {
23
- const extractedProvidersResult = familiesToUnifontProviders({ families, hashString });
24
- families = extractedProvidersResult.families;
25
- const { resolveFont } = await unifont.createUnifont(extractedProvidersResult.providers, {
26
- storage
27
- });
28
- for (const family of families) {
29
- const preloadData = [];
30
- let css = "";
31
- const fallbacks = family.fallbacks ?? DEFAULTS.fallbacks;
32
- const fallbackFontData = [];
33
- const collect = ({ hash, type, value, data }, collectPreload) => {
34
- const url = base + hash;
35
- if (!hashToUrlMap.has(hash)) {
36
- hashToUrlMap.set(hash, value);
37
- if (collectPreload) {
38
- preloadData.push({ url, type });
39
- }
40
- }
41
- if (fallbacks && fallbacks.length > 0 && // If the same data has already been sent for this family, we don't want to have duplicate fallbacks
42
- // Such scenario can occur with unicode ranges
43
- !fallbackFontData.some((f) => JSON.stringify(f.data) === JSON.stringify(data))) {
44
- fallbackFontData.push({
45
- hash,
46
- url: value,
47
- data
48
- });
49
- }
50
- return url;
51
- };
52
- let fonts;
53
- if (family.provider === LOCAL_PROVIDER_NAME) {
54
- const result = resolveLocalFont({
55
- family,
56
- proxyURL: ({ value, data }) => {
57
- return proxyURL({
58
- value,
59
- // We hash based on the filepath and the contents, since the user could replace
60
- // a given font file with completely different contents.
61
- hashString: (v) => {
62
- let content;
63
- try {
64
- content = readFileSync(value, "utf-8");
65
- } catch (e) {
66
- throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: e });
67
- }
68
- return hashString(v + content);
69
- },
70
- collect: (input) => collect({ ...input, data }, true)
71
- });
72
- }
73
- });
74
- fonts = result.fonts;
75
- } else {
76
- const result = await resolveFont(
77
- family.name,
78
- // We do not merge the defaults, we only provide defaults as a fallback
79
- {
80
- weights: family.weights ?? DEFAULTS.weights,
81
- styles: family.styles ?? DEFAULTS.styles,
82
- subsets: family.subsets ?? DEFAULTS.subsets,
83
- fallbacks: family.fallbacks ?? DEFAULTS.fallbacks
84
- },
85
- // By default, unifont goes through all providers. We use a different approach
86
- // where we specify a provider per font.
87
- // Name has been set while extracting unifont providers from families (inside familiesToUnifontProviders)
88
- [family.provider.name]
89
- );
90
- fonts = result.fonts.filter(
91
- (font) => typeof font.meta?.priority === "number" ? font.meta.priority === 0 : true
92
- ).map((font) => {
93
- let index = 0;
94
- return {
95
- ...font,
96
- src: font.src.map((source) => {
97
- if ("name" in source) {
98
- return source;
99
- }
100
- const proxied = {
101
- ...source,
102
- originalURL: source.url,
103
- url: proxyURL({
104
- value: source.url,
105
- // We only use the url for hashing since the service returns urls with a hash already
106
- hashString,
107
- // We only collect the first URL to avoid preloading fallback sources (eg. we only
108
- // preload woff2 if woff is available)
109
- collect: (data) => collect(
110
- {
111
- ...data,
112
- data: {
113
- weight: font.weight,
114
- style: font.style
115
- }
116
- },
117
- index === 0
118
- )
119
- })
120
- };
121
- index++;
122
- return proxied;
123
- })
124
- };
125
- });
126
- }
127
- for (const data of fonts) {
128
- css += generateFontFace(family.nameWithHash, {
129
- src: data.src,
130
- display: data.display ?? family.provider === LOCAL_PROVIDER_NAME ? void 0 : family.display,
131
- unicodeRange: data.unicodeRange ?? family.provider === LOCAL_PROVIDER_NAME ? void 0 : family.unicodeRange,
132
- weight: data.weight,
133
- style: data.style,
134
- stretch: data.stretch ?? family.provider === LOCAL_PROVIDER_NAME ? void 0 : family.stretch,
135
- featureSettings: data.featureSettings ?? family.provider === LOCAL_PROVIDER_NAME ? void 0 : family.featureSettings,
136
- variationSettings: data.variationSettings ?? family.provider === LOCAL_PROVIDER_NAME ? void 0 : family.variationSettings
137
- });
138
- }
139
- const fallbackData = await generateFallbacksCSS({
140
- family,
141
- font: fallbackFontData,
142
- fallbacks,
143
- metrics: family.optimizedFallbacks ?? DEFAULTS.optimizedFallbacks ? {
144
- getMetricsForFamily,
145
- generateFontFace: generateFallbackFontFace
146
- } : null
147
- });
148
- const cssVarValues = [family.nameWithHash];
149
- if (fallbackData) {
150
- if (fallbackData.css) {
151
- css += fallbackData.css;
152
- }
153
- cssVarValues.push(...fallbackData.fallbacks);
154
- }
155
- css += `:root { ${family.cssVariable}: ${cssVarValues.join(", ")}; }`;
156
- resolvedMap.set(family.cssVariable, { preloadData, css });
157
- }
158
- log("Fonts initialized");
159
- }
160
- export {
161
- loadFonts
162
- };
@@ -1,10 +0,0 @@
1
- import { type Font } from '@capsizecss/unpack';
2
- export type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
3
- export declare function readMetrics(family: string, buffer: Buffer): Promise<FontFaceMetrics>;
4
- export declare function generateFallbackFontFace({ metrics, fallbackMetrics, name: fallbackName, font: fallbackFontName, properties, }: {
5
- metrics: FontFaceMetrics;
6
- fallbackMetrics: FontFaceMetrics;
7
- name: string;
8
- font: string;
9
- properties: Record<string, string | undefined>;
10
- }): string;
@@ -1,55 +0,0 @@
1
- import { fromBuffer } from "@capsizecss/unpack";
2
- import { renderFontFace, renderFontSrc } from "./utils.js";
3
- const metricCache = {};
4
- function filterRequiredMetrics({
5
- ascent,
6
- descent,
7
- lineGap,
8
- unitsPerEm,
9
- xWidthAvg
10
- }) {
11
- return {
12
- ascent,
13
- descent,
14
- lineGap,
15
- unitsPerEm,
16
- xWidthAvg
17
- };
18
- }
19
- async function readMetrics(family, buffer) {
20
- const metrics = await fromBuffer(buffer);
21
- metricCache[family] = filterRequiredMetrics(metrics);
22
- return metricCache[family];
23
- }
24
- function toPercentage(value, fractionDigits = 4) {
25
- const percentage = value * 100;
26
- return `${+percentage.toFixed(fractionDigits)}%`;
27
- }
28
- function generateFallbackFontFace({
29
- metrics,
30
- fallbackMetrics,
31
- name: fallbackName,
32
- font: fallbackFontName,
33
- properties
34
- }) {
35
- const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
36
- const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
37
- const sizeAdjust = preferredFontXAvgRatio / fallbackFontXAvgRatio;
38
- const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust;
39
- const ascentOverride = metrics.ascent / adjustedEmSquare;
40
- const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare;
41
- const lineGapOverride = metrics.lineGap / adjustedEmSquare;
42
- return renderFontFace({
43
- "font-family": fallbackName,
44
- src: renderFontSrc([{ name: fallbackFontName }]),
45
- "size-adjust": toPercentage(sizeAdjust),
46
- "ascent-override": toPercentage(ascentOverride),
47
- "descent-override": toPercentage(descentOverride),
48
- "line-gap-override": toPercentage(lineGapOverride),
49
- ...properties
50
- });
51
- }
52
- export {
53
- generateFallbackFontFace,
54
- readMetrics
55
- };