astro 5.7.2 → 5.7.4

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.
@@ -160,13 +160,15 @@ function getActionContext(context) {
160
160
  }
161
161
  throw e;
162
162
  }
163
- const {
164
- props: _props,
165
- getActionResult: _getActionResult,
166
- callAction: _callAction,
167
- redirect: _redirect,
168
- ...actionAPIContext
169
- } = context;
163
+ const omitKeys = ["props", "getActionResult", "callAction", "redirect"];
164
+ const actionAPIContext = Object.create(
165
+ Object.getPrototypeOf(context),
166
+ Object.fromEntries(
167
+ Object.entries(Object.getOwnPropertyDescriptors(context)).filter(
168
+ ([key]) => !omitKeys.includes(key)
169
+ )
170
+ )
171
+ );
170
172
  Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true);
171
173
  const handler = baseAction.bind(actionAPIContext);
172
174
  return handler(input);
@@ -11,6 +11,7 @@ export declare const RESOLVED_VIRTUAL_MODULE_ID: string;
11
11
  export declare const URL_PREFIX = "/_astro/fonts/";
12
12
  export declare const CACHE_DIR = "./fonts/";
13
13
  export declare const FONT_TYPES: readonly ["woff2", "woff", "otf", "ttf", "eot"];
14
+ export declare const FONT_FORMAT_MAP: Record<(typeof FONT_TYPES)[number], string>;
14
15
  export declare const SYSTEM_METRICS: {
15
16
  'Times New Roman': {
16
17
  ascent: number;
@@ -12,6 +12,13 @@ const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
12
12
  const URL_PREFIX = "/_astro/fonts/";
13
13
  const CACHE_DIR = "./fonts/";
14
14
  const FONT_TYPES = ["woff2", "woff", "otf", "ttf", "eot"];
15
+ const FONT_FORMAT_MAP = {
16
+ woff2: "woff2",
17
+ woff: "woff",
18
+ otf: "opentype",
19
+ ttf: "truetype",
20
+ eot: "embedded-opentype"
21
+ };
15
22
  const SYSTEM_METRICS = {
16
23
  "Times New Roman": {
17
24
  ascent: 1825,
@@ -84,6 +91,7 @@ export {
84
91
  DEFAULTS,
85
92
  DEFAULT_FALLBACKS,
86
93
  FONTS_TYPES_FILE,
94
+ FONT_FORMAT_MAP,
87
95
  FONT_TYPES,
88
96
  LOCAL_PROVIDER_NAME,
89
97
  RESOLVED_VIRTUAL_MODULE_ID,
@@ -28,18 +28,24 @@ async function loadFonts({
28
28
  for (const family of families) {
29
29
  const preloadData = [];
30
30
  let css = "";
31
- let fallbackFontData = null;
32
- const collect = ({ hash, type, value }) => {
31
+ const fallbacks = family.fallbacks ?? DEFAULTS.fallbacks;
32
+ const fallbackFontData = [];
33
+ const collect = ({ hash, type, value, data }, collectPreload) => {
33
34
  const url = base + hash;
34
35
  if (!hashToUrlMap.has(hash)) {
35
36
  hashToUrlMap.set(hash, value);
36
- preloadData.push({ url, type });
37
+ if (collectPreload) {
38
+ preloadData.push({ url, type });
39
+ }
37
40
  }
38
- if (family.fallbacks && family.fallbacks.length > 0) {
39
- fallbackFontData ??= {
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({
40
45
  hash,
41
- url: value
42
- };
46
+ url: value,
47
+ data
48
+ });
43
49
  }
44
50
  return url;
45
51
  };
@@ -47,7 +53,7 @@ async function loadFonts({
47
53
  if (family.provider === LOCAL_PROVIDER_NAME) {
48
54
  const result = resolveLocalFont({
49
55
  family,
50
- proxyURL: (value) => {
56
+ proxyURL: ({ value, data }) => {
51
57
  return proxyURL({
52
58
  value,
53
59
  // We hash based on the filepath and the contents, since the user could replace
@@ -61,7 +67,7 @@ async function loadFonts({
61
67
  }
62
68
  return hashString(v + content);
63
69
  },
64
- collect
70
+ collect: (input) => collect({ ...input, data }, true)
65
71
  });
66
72
  }
67
73
  });
@@ -83,21 +89,40 @@ async function loadFonts({
83
89
  );
84
90
  fonts = result.fonts.filter(
85
91
  (font) => typeof font.meta?.priority === "number" ? font.meta.priority === 0 : true
86
- ).map((font) => ({
87
- ...font,
88
- src: font.src.slice(0, 1).map(
89
- (source) => "name" in source ? source : {
90
- ...source,
91
- originalURL: source.url,
92
- url: proxyURL({
93
- value: source.url,
94
- // We only use the url for hashing since the service returns urls with a hash already
95
- hashString,
96
- collect
97
- })
98
- }
99
- )
100
- }));
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
+ });
101
126
  }
102
127
  for (const data of fonts) {
103
128
  css += generateFontFace(family.nameWithHash, {
@@ -114,7 +139,7 @@ async function loadFonts({
114
139
  const fallbackData = await generateFallbacksCSS({
115
140
  family,
116
141
  font: fallbackFontData,
117
- fallbacks: family.fallbacks ?? DEFAULTS.fallbacks,
142
+ fallbacks,
118
143
  metrics: family.optimizedFallbacks ?? DEFAULTS.optimizedFallbacks ? {
119
144
  getMetricsForFamily,
120
145
  generateFontFace: generateFallbackFontFace
@@ -122,7 +147,9 @@ async function loadFonts({
122
147
  });
123
148
  const cssVarValues = [family.nameWithHash];
124
149
  if (fallbackData) {
125
- css += fallbackData.css;
150
+ if (fallbackData.css) {
151
+ css += fallbackData.css;
152
+ }
126
153
  cssVarValues.push(...fallbackData.fallbacks);
127
154
  }
128
155
  css += `:root { ${family.cssVariable}: ${cssVarValues.join(", ")}; }`;
@@ -6,5 +6,5 @@ export declare function generateFallbackFontFace({ metrics, fallbackMetrics, nam
6
6
  fallbackMetrics: FontFaceMetrics;
7
7
  name: string;
8
8
  font: string;
9
- properties?: Record<string, string | undefined>;
9
+ properties: Record<string, string | undefined>;
10
10
  }): string;
@@ -30,7 +30,7 @@ function generateFallbackFontFace({
30
30
  fallbackMetrics,
31
31
  name: fallbackName,
32
32
  font: fallbackFontName,
33
- properties = {}
33
+ properties
34
34
  }) {
35
35
  const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
36
36
  const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
@@ -4,7 +4,10 @@ type InitializedProvider = NonNullable<Awaited<ReturnType<unifont.Provider>>>;
4
4
  type ResolveFontResult = NonNullable<Awaited<ReturnType<InitializedProvider['resolveFont']>>>;
5
5
  interface Options {
6
6
  family: ResolvedLocalFontFamily;
7
- proxyURL: (value: string) => string;
7
+ proxyURL: (params: {
8
+ value: string;
9
+ data: Partial<unifont.FontFaceData>;
10
+ }) => string;
8
11
  }
9
12
  export declare function resolveLocalFont({ family, proxyURL }: Options): ResolveFontResult;
10
13
  export {};
@@ -1,3 +1,4 @@
1
+ import { FONT_FORMAT_MAP } from "../constants.js";
1
2
  import { extractFontType } from "../utils.js";
2
3
  function resolveLocalFont({ family, proxyURL }) {
3
4
  const fonts = [];
@@ -8,8 +9,14 @@ function resolveLocalFont({ family, proxyURL }) {
8
9
  src: variant.src.map(({ url: originalURL, tech }) => {
9
10
  return {
10
11
  originalURL,
11
- url: proxyURL(originalURL),
12
- format: extractFontType(originalURL),
12
+ url: proxyURL({
13
+ value: originalURL,
14
+ data: {
15
+ weight: variant.weight,
16
+ style: variant.style
17
+ }
18
+ }),
19
+ format: FONT_FORMAT_MAP[extractFontType(originalURL)],
13
20
  tech
14
21
  };
15
22
  })
@@ -6,6 +6,7 @@ import { type ResolveProviderOptions } from './providers/utils.js';
6
6
  import type { FontFamily, FontType, ResolvedFontFamily } from './types.js';
7
7
  export declare function toCSS(properties: Record<string, string | undefined>, indent?: number): string;
8
8
  export declare function renderFontFace(properties: Record<string, string | undefined>): string;
9
+ export declare function unifontFontFaceDataToProperties(font: Partial<unifont.FontFaceData>): Record<string, string | undefined>;
9
10
  export declare function generateFontFace(family: string, font: unifont.FontFaceData): string;
10
11
  export declare function renderFontSrc(sources: Exclude<unifont.FontFaceData['src'][number], string>[]): string;
11
12
  export declare function withoutQuotes(str: string): string;
@@ -46,6 +47,7 @@ export declare function isGenericFontFamily(str: string): str is keyof typeof DE
46
47
  export type GetMetricsForFamilyFont = {
47
48
  hash: string;
48
49
  url: string;
50
+ data: Partial<unifont.FontFaceData>;
49
51
  };
50
52
  export type GetMetricsForFamily = (name: string,
51
53
  /** A remote url or local filepath to a font file. Used if metrics can't be resolved purely from the family name */
@@ -61,13 +63,13 @@ export declare function generateFallbacksCSS({ family, fallbacks: _fallbacks, fo
61
63
  family: Pick<ResolvedFontFamily, 'name' | 'nameWithHash'>;
62
64
  /** The family fallbacks */
63
65
  fallbacks: Array<string>;
64
- font: GetMetricsForFamilyFont | null;
66
+ font: Array<GetMetricsForFamilyFont>;
65
67
  metrics: {
66
68
  getMetricsForFamily: GetMetricsForFamily;
67
69
  generateFontFace: typeof generateFallbackFontFace;
68
70
  } | null;
69
71
  }): Promise<null | {
70
- css: string;
72
+ css?: string;
71
73
  fallbacks: Array<string>;
72
74
  }>;
73
75
  /**
@@ -13,10 +13,9 @@ function renderFontFace(properties) {
13
13
  }
14
14
  `;
15
15
  }
16
- function generateFontFace(family, font) {
17
- return renderFontFace({
18
- "font-family": family,
19
- src: renderFontSrc(font.src),
16
+ function unifontFontFaceDataToProperties(font) {
17
+ return {
18
+ src: font.src ? renderFontSrc(font.src) : void 0,
20
19
  "font-display": font.display ?? "swap",
21
20
  "unicode-range": font.unicodeRange?.join(","),
22
21
  "font-weight": Array.isArray(font.weight) ? font.weight.join(" ") : font.weight?.toString(),
@@ -24,17 +23,23 @@ function generateFontFace(family, font) {
24
23
  "font-stretch": font.stretch,
25
24
  "font-feature-settings": font.featureSettings,
26
25
  "font-variation-settings": font.variationSettings
26
+ };
27
+ }
28
+ function generateFontFace(family, font) {
29
+ return renderFontFace({
30
+ "font-family": family,
31
+ ...unifontFontFaceDataToProperties(font)
27
32
  });
28
33
  }
29
34
  function renderFontSrc(sources) {
30
35
  return sources.map((src) => {
31
36
  if ("url" in src) {
32
37
  let rendered = `url("${src.url}")`;
33
- for (const key of ["format", "tech"]) {
34
- const value = src[key];
35
- if (value) {
36
- rendered += ` ${key}(${value})`;
37
- }
38
+ if (src.format) {
39
+ rendered += ` format("${src.format}")`;
40
+ }
41
+ if (src.tech) {
42
+ rendered += ` tech(${src.tech})`;
38
43
  }
39
44
  return rendered;
40
45
  }
@@ -91,35 +96,39 @@ async function generateFallbacksCSS({
91
96
  if (fallbacks.length === 0) {
92
97
  return null;
93
98
  }
94
- let css = "";
95
- if (!fontData || !metrics) {
96
- return { css, fallbacks };
99
+ if (fontData.length === 0 || !metrics) {
100
+ return { fallbacks };
97
101
  }
98
102
  const lastFallback = fallbacks[fallbacks.length - 1];
99
103
  if (!isGenericFontFamily(lastFallback)) {
100
- return { css, fallbacks };
104
+ return { fallbacks };
101
105
  }
102
106
  const localFonts = DEFAULT_FALLBACKS[lastFallback];
103
107
  if (localFonts.length === 0) {
104
- return { css, fallbacks };
108
+ return { fallbacks };
105
109
  }
106
- const foundMetrics = await metrics.getMetricsForFamily(family.name, fontData);
107
- if (!foundMetrics) {
108
- return { css, fallbacks };
110
+ if (localFonts.includes(
111
+ // @ts-expect-error TS is not smart enough
112
+ family.name
113
+ )) {
114
+ return { fallbacks };
109
115
  }
110
116
  const localFontsMappings = localFonts.map((font) => ({
111
117
  font,
112
118
  name: `"${family.nameWithHash} fallback: ${font}"`
113
119
  }));
114
120
  fallbacks = [.../* @__PURE__ */ new Set([...localFontsMappings.map((m) => m.name), ...fallbacks])];
121
+ let css = "";
115
122
  for (const { font, name } of localFontsMappings) {
116
- css += metrics.generateFontFace({
117
- metrics: foundMetrics,
118
- fallbackMetrics: SYSTEM_METRICS[font],
119
- font,
120
- name
121
- // TODO: forward some properties once we generate one fallback per font face data
122
- });
123
+ for (const { hash, url, data } of fontData) {
124
+ css += metrics.generateFontFace({
125
+ metrics: await metrics.getMetricsForFamily(family.name, { hash, url, data }),
126
+ fallbackMetrics: SYSTEM_METRICS[font],
127
+ font,
128
+ name,
129
+ properties: unifontFontFaceDataToProperties(data)
130
+ });
131
+ }
123
132
  }
124
133
  return { css, fallbacks };
125
134
  }
@@ -200,13 +209,12 @@ function familiesToUnifontProviders({
200
209
  })
201
210
  )
202
211
  );
203
- if (hashes.has(hash)) {
204
- continue;
205
- }
206
212
  unifontProvider._name += `-${hash}`;
207
213
  provider.name = unifontProvider._name;
208
- hashes.add(hash);
209
- providers.push(unifontProvider);
214
+ if (!hashes.has(hash)) {
215
+ hashes.add(hash);
216
+ providers.push(unifontProvider);
217
+ }
210
218
  }
211
219
  return { families, providers };
212
220
  }
@@ -233,5 +241,6 @@ export {
233
241
  resolveFontFamily,
234
242
  sortObjectByKey,
235
243
  toCSS,
244
+ unifontFontFaceDataToProperties,
236
245
  withoutQuotes
237
246
  };
@@ -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.2") {
156
+ if (previousAstroVersion && previousAstroVersion !== "5.7.4") {
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.2") {
165
- await this.#store.metaStore().set("astro-version", "5.7.2");
164
+ if ("5.7.4") {
165
+ await this.#store.metaStore().set("astro-version", "5.7.4");
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.2";
1
+ const ASTRO_VERSION = "5.7.4";
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.2";
25
+ const currentVersion = "5.7.4";
26
26
  const isPrerelease = currentVersion.includes("-");
27
27
  if (!isPrerelease) {
28
28
  try {
@@ -38,7 +38,7 @@ function serverStart({
38
38
  host,
39
39
  base
40
40
  }) {
41
- const version = "5.7.2";
41
+ const version = "5.7.4";
42
42
  const localPrefix = `${dim("\u2503")} Local `;
43
43
  const networkPrefix = `${dim("\u2503")} Network `;
44
44
  const emptyPrefix = " ".repeat(11);
@@ -282,7 +282,7 @@ function printHelp({
282
282
  message.push(
283
283
  linebreak(),
284
284
  ` ${bgGreen(black(` ${commandName} `))} ${green(
285
- `v${"5.7.2"}`
285
+ `v${"5.7.4"}`
286
286
  )} ${headline}`
287
287
  );
288
288
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "5.7.2",
3
+ "version": "5.7.4",
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",
@@ -160,9 +160,9 @@
160
160
  "zod": "^3.24.2",
161
161
  "zod-to-json-schema": "^3.24.5",
162
162
  "zod-to-ts": "^1.2.0",
163
- "@astrojs/internal-helpers": "0.6.1",
163
+ "@astrojs/markdown-remark": "6.3.1",
164
164
  "@astrojs/telemetry": "3.2.0",
165
- "@astrojs/markdown-remark": "6.3.1"
165
+ "@astrojs/internal-helpers": "0.6.1"
166
166
  },
167
167
  "optionalDependencies": {
168
168
  "sharp": "^0.33.3"