astro 5.7.1 → 5.7.2

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.
@@ -11,5 +11,70 @@ 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 DEFAULT_FALLBACKS: Record<string, Array<string>>;
14
+ export declare const SYSTEM_METRICS: {
15
+ 'Times New Roman': {
16
+ ascent: number;
17
+ descent: number;
18
+ lineGap: number;
19
+ unitsPerEm: number;
20
+ xWidthAvg: number;
21
+ };
22
+ Arial: {
23
+ ascent: number;
24
+ descent: number;
25
+ lineGap: number;
26
+ unitsPerEm: number;
27
+ xWidthAvg: number;
28
+ };
29
+ 'Courier New': {
30
+ ascent: number;
31
+ descent: number;
32
+ lineGap: number;
33
+ unitsPerEm: number;
34
+ xWidthAvg: number;
35
+ };
36
+ BlinkMacSystemFont: {
37
+ ascent: number;
38
+ descent: number;
39
+ lineGap: number;
40
+ unitsPerEm: number;
41
+ xWidthAvg: number;
42
+ };
43
+ 'Segoe UI': {
44
+ ascent: number;
45
+ descent: number;
46
+ lineGap: number;
47
+ unitsPerEm: number;
48
+ xWidthAvg: number;
49
+ };
50
+ Roboto: {
51
+ ascent: number;
52
+ descent: number;
53
+ lineGap: number;
54
+ unitsPerEm: number;
55
+ xWidthAvg: number;
56
+ };
57
+ 'Helvetica Neue': {
58
+ ascent: number;
59
+ descent: number;
60
+ lineGap: number;
61
+ unitsPerEm: number;
62
+ xWidthAvg: number;
63
+ };
64
+ };
65
+ export declare const DEFAULT_FALLBACKS: {
66
+ readonly serif: ["Times New Roman"];
67
+ readonly 'sans-serif': ["Arial"];
68
+ readonly monospace: ["Courier New"];
69
+ readonly cursive: [];
70
+ readonly fantasy: [];
71
+ readonly 'system-ui': ["BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial"];
72
+ readonly 'ui-serif': ["Times New Roman"];
73
+ readonly 'ui-sans-serif': ["Arial"];
74
+ readonly 'ui-monospace': ["Courier New"];
75
+ readonly 'ui-rounded': [];
76
+ readonly emoji: [];
77
+ readonly math: [];
78
+ readonly fangsong: [];
79
+ };
15
80
  export declare const FONTS_TYPES_FILE = "fonts.d.ts";
@@ -12,6 +12,57 @@ 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 SYSTEM_METRICS = {
16
+ "Times New Roman": {
17
+ ascent: 1825,
18
+ descent: -443,
19
+ lineGap: 87,
20
+ unitsPerEm: 2048,
21
+ xWidthAvg: 832
22
+ },
23
+ Arial: {
24
+ ascent: 1854,
25
+ descent: -434,
26
+ lineGap: 67,
27
+ unitsPerEm: 2048,
28
+ xWidthAvg: 913
29
+ },
30
+ "Courier New": {
31
+ ascent: 1705,
32
+ descent: -615,
33
+ lineGap: 0,
34
+ unitsPerEm: 2048,
35
+ xWidthAvg: 1229
36
+ },
37
+ BlinkMacSystemFont: {
38
+ ascent: 1980,
39
+ descent: -432,
40
+ lineGap: 0,
41
+ unitsPerEm: 2048,
42
+ xWidthAvg: 853
43
+ },
44
+ "Segoe UI": {
45
+ ascent: 2210,
46
+ descent: -514,
47
+ lineGap: 0,
48
+ unitsPerEm: 2048,
49
+ xWidthAvg: 908
50
+ },
51
+ Roboto: {
52
+ ascent: 1900,
53
+ descent: -500,
54
+ lineGap: 0,
55
+ unitsPerEm: 2048,
56
+ xWidthAvg: 911
57
+ },
58
+ "Helvetica Neue": {
59
+ ascent: 952,
60
+ descent: -213,
61
+ lineGap: 28,
62
+ unitsPerEm: 1e3,
63
+ xWidthAvg: 450
64
+ }
65
+ };
15
66
  const DEFAULT_FALLBACKS = {
16
67
  serif: ["Times New Roman"],
17
68
  "sans-serif": ["Arial"],
@@ -36,6 +87,7 @@ export {
36
87
  FONT_TYPES,
37
88
  LOCAL_PROVIDER_NAME,
38
89
  RESOLVED_VIRTUAL_MODULE_ID,
90
+ SYSTEM_METRICS,
39
91
  URL_PREFIX,
40
92
  VIRTUAL_MODULE_ID
41
93
  };
@@ -1,9 +1,10 @@
1
1
  import { type Font } from '@capsizecss/unpack';
2
2
  export type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
3
3
  export declare function readMetrics(family: string, buffer: Buffer): Promise<FontFaceMetrics>;
4
- export declare function generateFallbackFontFace(metrics: FontFaceMetrics, fallback: {
4
+ export declare function generateFallbackFontFace({ metrics, fallbackMetrics, name: fallbackName, font: fallbackFontName, properties, }: {
5
+ metrics: FontFaceMetrics;
6
+ fallbackMetrics: FontFaceMetrics;
5
7
  name: string;
6
8
  font: string;
7
- metrics?: FontFaceMetrics;
8
- [key: string]: any;
9
+ properties?: Record<string, string | undefined>;
9
10
  }): string;
@@ -1,4 +1,5 @@
1
1
  import { fromBuffer } from "@capsizecss/unpack";
2
+ import { renderFontFace, renderFontSrc } from "./utils.js";
2
3
  const metricCache = {};
3
4
  function filterRequiredMetrics({
4
5
  ascent,
@@ -24,36 +25,29 @@ function toPercentage(value, fractionDigits = 4) {
24
25
  const percentage = value * 100;
25
26
  return `${+percentage.toFixed(fractionDigits)}%`;
26
27
  }
27
- function toCSS(properties, indent = 2) {
28
- return Object.entries(properties).map(([key, value]) => `${" ".repeat(indent)}${key}: ${value};`).join("\n");
29
- }
30
- function generateFallbackFontFace(metrics, fallback) {
31
- const {
32
- name: fallbackName,
33
- font: fallbackFontName,
34
- metrics: fallbackMetrics,
35
- ...properties
36
- } = fallback;
28
+ function generateFallbackFontFace({
29
+ metrics,
30
+ fallbackMetrics,
31
+ name: fallbackName,
32
+ font: fallbackFontName,
33
+ properties = {}
34
+ }) {
37
35
  const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
38
- const fallbackFontXAvgRatio = fallbackMetrics ? fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm : 1;
39
- const sizeAdjust = fallbackMetrics && preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1;
36
+ const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
37
+ const sizeAdjust = preferredFontXAvgRatio / fallbackFontXAvgRatio;
40
38
  const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust;
41
39
  const ascentOverride = metrics.ascent / adjustedEmSquare;
42
40
  const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare;
43
41
  const lineGapOverride = metrics.lineGap / adjustedEmSquare;
44
- const declaration = {
45
- "font-family": JSON.stringify(fallbackName),
46
- src: `local(${JSON.stringify(fallbackFontName)})`,
42
+ return renderFontFace({
43
+ "font-family": fallbackName,
44
+ src: renderFontSrc([{ name: fallbackFontName }]),
47
45
  "size-adjust": toPercentage(sizeAdjust),
48
46
  "ascent-override": toPercentage(ascentOverride),
49
47
  "descent-override": toPercentage(descentOverride),
50
48
  "line-gap-override": toPercentage(lineGapOverride),
51
49
  ...properties
52
- };
53
- return `@font-face {
54
- ${toCSS(declaration)}
55
- }
56
- `;
50
+ });
57
51
  }
58
52
  export {
59
53
  generateFallbackFontFace,
@@ -4,7 +4,11 @@ import { DEFAULT_FALLBACKS } from './constants.js';
4
4
  import type { FontFaceMetrics, generateFallbackFontFace } from './metrics.js';
5
5
  import { type ResolveProviderOptions } from './providers/utils.js';
6
6
  import type { FontFamily, FontType, ResolvedFontFamily } from './types.js';
7
+ export declare function toCSS(properties: Record<string, string | undefined>, indent?: number): string;
8
+ export declare function renderFontFace(properties: Record<string, string | undefined>): string;
7
9
  export declare function generateFontFace(family: string, font: unifont.FontFaceData): string;
10
+ export declare function renderFontSrc(sources: Exclude<unifont.FontFaceData['src'][number], string>[]): string;
11
+ export declare function withoutQuotes(str: string): string;
8
12
  export declare function extractFontType(str: string): FontType;
9
13
  export declare function isFontType(str: string): str is FontType;
10
14
  export declare function cache(storage: Storage, key: string, cb: () => Promise<Buffer>): Promise<Buffer>;
@@ -2,30 +2,38 @@ import { createRequire } from "node:module";
2
2
  import { extname } from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import { AstroError, AstroErrorData } from "../../core/errors/index.js";
5
- import { DEFAULT_FALLBACKS, FONT_TYPES, LOCAL_PROVIDER_NAME } from "./constants.js";
5
+ import { DEFAULT_FALLBACKS, FONT_TYPES, LOCAL_PROVIDER_NAME, SYSTEM_METRICS } from "./constants.js";
6
6
  import { resolveProvider } from "./providers/utils.js";
7
+ function toCSS(properties, indent = 2) {
8
+ return Object.entries(properties).filter(([, value]) => Boolean(value)).map(([key, value]) => `${" ".repeat(indent)}${key}: ${value};`).join("\n");
9
+ }
10
+ function renderFontFace(properties) {
11
+ return `@font-face {
12
+ ${toCSS(properties)}
13
+ }
14
+ `;
15
+ }
7
16
  function generateFontFace(family, font) {
8
- return [
9
- "@font-face {",
10
- ` font-family: ${family};`,
11
- ` src: ${renderFontSrc(font.src)};`,
12
- ` font-display: ${font.display ?? "swap"};`,
13
- font.unicodeRange && ` unicode-range: ${font.unicodeRange};`,
14
- font.weight && ` font-weight: ${Array.isArray(font.weight) ? font.weight.join(" ") : font.weight};`,
15
- font.style && ` font-style: ${font.style};`,
16
- font.stretch && ` font-stretch: ${font.stretch};`,
17
- font.featureSettings && ` font-feature-settings: ${font.featureSettings};`,
18
- font.variationSettings && ` font-variation-settings: ${font.variationSettings};`,
19
- `}`
20
- ].filter(Boolean).join("\n");
17
+ return renderFontFace({
18
+ "font-family": family,
19
+ src: renderFontSrc(font.src),
20
+ "font-display": font.display ?? "swap",
21
+ "unicode-range": font.unicodeRange?.join(","),
22
+ "font-weight": Array.isArray(font.weight) ? font.weight.join(" ") : font.weight?.toString(),
23
+ "font-style": font.style,
24
+ "font-stretch": font.stretch,
25
+ "font-feature-settings": font.featureSettings,
26
+ "font-variation-settings": font.variationSettings
27
+ });
21
28
  }
22
29
  function renderFontSrc(sources) {
23
30
  return sources.map((src) => {
24
31
  if ("url" in src) {
25
32
  let rendered = `url("${src.url}")`;
26
33
  for (const key of ["format", "tech"]) {
27
- if (key in src) {
28
- rendered += ` ${key}(${src[key]})`;
34
+ const value = src[key];
35
+ if (value) {
36
+ rendered += ` ${key}(${value})`;
29
37
  }
30
38
  }
31
39
  return rendered;
@@ -33,6 +41,10 @@ function renderFontSrc(sources) {
33
41
  return `local("${src.name}")`;
34
42
  }).join(", ");
35
43
  }
44
+ const QUOTES_RE = /^["']|["']$/g;
45
+ function withoutQuotes(str) {
46
+ return str.trim().replace(QUOTES_RE, "");
47
+ }
36
48
  function extractFontType(str) {
37
49
  const extension = extname(str).slice(1);
38
50
  if (!isFontType(extension)) {
@@ -101,7 +113,13 @@ async function generateFallbacksCSS({
101
113
  }));
102
114
  fallbacks = [.../* @__PURE__ */ new Set([...localFontsMappings.map((m) => m.name), ...fallbacks])];
103
115
  for (const { font, name } of localFontsMappings) {
104
- css += metrics.generateFontFace(foundMetrics, { font, name });
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
+ });
105
123
  }
106
124
  return { css, fallbacks };
107
125
  }
@@ -209,7 +227,11 @@ export {
209
227
  isFontType,
210
228
  isGenericFontFamily,
211
229
  proxyURL,
230
+ renderFontFace,
231
+ renderFontSrc,
212
232
  resolveEntrypoint,
213
233
  resolveFontFamily,
214
- sortObjectByKey
234
+ sortObjectByKey,
235
+ toCSS,
236
+ withoutQuotes
215
237
  };
@@ -18,7 +18,13 @@ import {
18
18
  } from "./constants.js";
19
19
  import { loadFonts } from "./load.js";
20
20
  import { generateFallbackFontFace, readMetrics } from "./metrics.js";
21
- import { cache, extractFontType, resolveFontFamily, sortObjectByKey } from "./utils.js";
21
+ import {
22
+ cache,
23
+ extractFontType,
24
+ resolveFontFamily,
25
+ sortObjectByKey,
26
+ withoutQuotes
27
+ } from "./utils.js";
22
28
  async function fetchFont(url) {
23
29
  try {
24
30
  if (isAbsolute(url)) {
@@ -81,7 +87,7 @@ function fontsPlugin({ settings, sync, logger }) {
81
87
  family,
82
88
  root: settings.config.root,
83
89
  resolveMod,
84
- generateNameWithHash: (_family) => `${_family.name}-${h64ToString(JSON.stringify(sortObjectByKey(_family)))}`
90
+ generateNameWithHash: (_family) => `${withoutQuotes(_family.name)}-${h64ToString(JSON.stringify(sortObjectByKey(_family)))}`
85
91
  })
86
92
  );
87
93
  }
@@ -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.1") {
156
+ if (previousAstroVersion && previousAstroVersion !== "5.7.2") {
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.1") {
165
- await this.#store.metaStore().set("astro-version", "5.7.1");
164
+ if ("5.7.2") {
165
+ await this.#store.metaStore().set("astro-version", "5.7.2");
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.1";
1
+ const ASTRO_VERSION = "5.7.2";
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.1";
25
+ const currentVersion = "5.7.2";
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.1";
41
+ const version = "5.7.2";
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.1"}`
285
+ `v${"5.7.2"}`
286
286
  )} ${headline}`
287
287
  );
288
288
  }
@@ -236,10 +236,8 @@ function createRedirectRoutes({ settings }, routeMap) {
236
236
  } else {
237
237
  destination = to.destination;
238
238
  }
239
- if (!destination.startsWith("/")) {
240
- if (!/^https?:\/\//.test(destination) && !URL.canParse(destination)) {
241
- throw new AstroError(UnsupportedExternalRedirect);
242
- }
239
+ if (URL.canParse(destination) && !/^https?:\/\//.test(destination)) {
240
+ throw new AstroError(UnsupportedExternalRedirect);
243
241
  }
244
242
  routes.push({
245
243
  type: "redirect",
@@ -39,7 +39,7 @@ function findRouteToRewrite({
39
39
  if (!pathname.startsWith("/") && shouldAppendSlash && newUrl.pathname.endsWith("/")) {
40
40
  pathname = prependForwardSlash(pathname);
41
41
  }
42
- if (pathname === "/" && !shouldAppendSlash) {
42
+ if (pathname === "/" && base !== "/" && !shouldAppendSlash) {
43
43
  pathname = "";
44
44
  }
45
45
  if (base !== "/" && (pathname === "" || pathname === "/") && !shouldAppendSlash) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "5.7.1",
3
+ "version": "5.7.2",
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
164
  "@astrojs/telemetry": "3.2.0",
164
- "@astrojs/markdown-remark": "6.3.1",
165
- "@astrojs/internal-helpers": "0.6.1"
165
+ "@astrojs/markdown-remark": "6.3.1"
166
166
  },
167
167
  "optionalDependencies": {
168
168
  "sharp": "^0.33.3"