astro 5.6.1 → 5.7.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.
Files changed (94) hide show
  1. package/client.d.ts +3 -0
  2. package/components/Font.astro +32 -0
  3. package/dist/actions/plugins.js +2 -2
  4. package/dist/assets/fonts/config.d.ts +378 -0
  5. package/dist/assets/fonts/config.js +157 -0
  6. package/dist/assets/fonts/constants.d.ts +15 -0
  7. package/dist/assets/fonts/constants.js +41 -0
  8. package/dist/assets/fonts/load.d.ts +20 -0
  9. package/dist/assets/fonts/load.js +133 -0
  10. package/dist/assets/fonts/metrics.d.ts +10 -0
  11. package/dist/assets/fonts/metrics.js +84 -0
  12. package/dist/assets/fonts/providers/entrypoints/adobe.d.ts +2 -0
  13. package/dist/assets/fonts/providers/entrypoints/adobe.js +5 -0
  14. package/dist/assets/fonts/providers/entrypoints/bunny.d.ts +1 -0
  15. package/dist/assets/fonts/providers/entrypoints/bunny.js +5 -0
  16. package/dist/assets/fonts/providers/entrypoints/fontshare.d.ts +1 -0
  17. package/dist/assets/fonts/providers/entrypoints/fontshare.js +5 -0
  18. package/dist/assets/fonts/providers/entrypoints/fontsource.d.ts +1 -0
  19. package/dist/assets/fonts/providers/entrypoints/fontsource.js +5 -0
  20. package/dist/assets/fonts/providers/entrypoints/google.d.ts +2 -0
  21. package/dist/assets/fonts/providers/entrypoints/google.js +5 -0
  22. package/dist/assets/fonts/providers/index.d.ts +48 -0
  23. package/dist/assets/fonts/providers/index.js +40 -0
  24. package/dist/assets/fonts/providers/local.d.ts +10 -0
  25. package/dist/assets/fonts/providers/local.js +30 -0
  26. package/dist/assets/fonts/providers/utils.d.ts +9 -0
  27. package/dist/assets/fonts/providers/utils.js +37 -0
  28. package/dist/assets/fonts/sync.d.ts +2 -0
  29. package/dist/assets/fonts/sync.js +17 -0
  30. package/dist/assets/fonts/types.d.ts +45 -0
  31. package/dist/assets/fonts/types.js +0 -0
  32. package/dist/assets/fonts/utils.d.ts +95 -0
  33. package/dist/assets/fonts/utils.js +215 -0
  34. package/dist/assets/fonts/vite-plugin-fonts.d.ts +10 -0
  35. package/dist/assets/fonts/vite-plugin-fonts.js +217 -0
  36. package/dist/assets/utils/index.d.ts +5 -1
  37. package/dist/assets/utils/index.js +5 -1
  38. package/dist/assets/utils/node/emitAsset.d.ts +12 -2
  39. package/dist/assets/utils/node/emitAsset.js +46 -4
  40. package/dist/assets/utils/vendor/image-size/types/index.d.ts +2 -2
  41. package/dist/assets/vite-plugin-assets.d.ts +9 -2
  42. package/dist/assets/vite-plugin-assets.js +9 -6
  43. package/dist/cli/add/index.js +3 -1
  44. package/dist/cli/install-package.js +3 -0
  45. package/dist/config/entrypoint.d.ts +2 -0
  46. package/dist/config/entrypoint.js +3 -0
  47. package/dist/config/index.d.ts +2 -1
  48. package/dist/content/content-layer.js +5 -4
  49. package/dist/content/runtime-assets.js +1 -0
  50. package/dist/content/utils.d.ts +10 -10
  51. package/dist/content/vite-plugin-content-imports.js +4 -2
  52. package/dist/core/build/generate.js +2 -2
  53. package/dist/core/build/pipeline.js +2 -2
  54. package/dist/core/build/plugins/plugin-prerender.js +0 -3
  55. package/dist/core/build/static-build.js +2 -2
  56. package/dist/core/config/schemas/base.d.ts +446 -49
  57. package/dist/core/config/schemas/base.js +3 -7
  58. package/dist/core/config/schemas/refined.js +12 -0
  59. package/dist/core/config/schemas/relative.d.ts +581 -77
  60. package/dist/core/config/schemas/relative.js +1 -2
  61. package/dist/core/constants.js +1 -1
  62. package/dist/core/create-vite.js +2 -2
  63. package/dist/core/dev/dev.js +1 -1
  64. package/dist/core/errors/errors-data.d.ts +82 -26
  65. package/dist/core/errors/errors-data.js +45 -16
  66. package/dist/core/logger/core.d.ts +1 -1
  67. package/dist/core/messages.js +2 -2
  68. package/dist/core/middleware/vite-plugin.js +2 -2
  69. package/dist/core/render-context.js +39 -5
  70. package/dist/core/routing/rewrite.js +14 -5
  71. package/dist/core/session.d.ts +2 -4
  72. package/dist/core/session.js +4 -29
  73. package/dist/core/sync/index.js +2 -0
  74. package/dist/env/schema.d.ts +6 -6
  75. package/dist/integrations/hooks.js +2 -3
  76. package/dist/manifest/virtual-module.d.ts +1 -5
  77. package/dist/manifest/virtual-module.js +1 -18
  78. package/dist/prerender/utils.d.ts +5 -1
  79. package/dist/prerender/utils.js +8 -8
  80. package/dist/runtime/client/dev-toolbar/apps/audit/annotations.d.ts +6 -0
  81. package/dist/runtime/client/dev-toolbar/apps/audit/annotations.js +27 -0
  82. package/dist/runtime/client/dev-toolbar/apps/audit/index.js +10 -4
  83. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.js +2 -2
  84. package/dist/runtime/server/render/common.js +8 -0
  85. package/dist/runtime/server/render/instruction.d.ts +5 -5
  86. package/dist/runtime/server/render/server-islands.d.ts +1 -0
  87. package/dist/runtime/server/render/server-islands.js +29 -31
  88. package/dist/transitions/swap-functions.d.ts +1 -1
  89. package/dist/transitions/swap-functions.js +7 -6
  90. package/dist/types/public/config.d.ts +155 -98
  91. package/dist/types/public/internal.d.ts +1 -0
  92. package/dist/vite-plugin-astro-server/plugin.js +1 -1
  93. package/package.json +9 -5
  94. package/types/fonts.d.ts +4 -0
@@ -0,0 +1,215 @@
1
+ import { createRequire } from "node:module";
2
+ import { extname } from "node:path";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { AstroError, AstroErrorData } from "../../core/errors/index.js";
5
+ import { DEFAULT_FALLBACKS, FONT_TYPES, LOCAL_PROVIDER_NAME } from "./constants.js";
6
+ import { resolveProvider } from "./providers/utils.js";
7
+ 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");
21
+ }
22
+ function renderFontSrc(sources) {
23
+ return sources.map((src) => {
24
+ if ("url" in src) {
25
+ let rendered = `url("${src.url}")`;
26
+ for (const key of ["format", "tech"]) {
27
+ if (key in src) {
28
+ rendered += ` ${key}(${src[key]})`;
29
+ }
30
+ }
31
+ return rendered;
32
+ }
33
+ return `local("${src.name}")`;
34
+ }).join(", ");
35
+ }
36
+ function extractFontType(str) {
37
+ const extension = extname(str).slice(1);
38
+ if (!isFontType(extension)) {
39
+ throw new AstroError(
40
+ {
41
+ ...AstroErrorData.CannotExtractFontType,
42
+ message: AstroErrorData.CannotExtractFontType.message(str)
43
+ },
44
+ {
45
+ cause: `Unexpected extension, got "${extension}"`
46
+ }
47
+ );
48
+ }
49
+ return extension;
50
+ }
51
+ function isFontType(str) {
52
+ return FONT_TYPES.includes(str);
53
+ }
54
+ async function cache(storage, key, cb) {
55
+ const existing = await storage.getItemRaw(key);
56
+ if (existing) {
57
+ return { cached: true, data: existing };
58
+ }
59
+ const data = await cb();
60
+ await storage.setItemRaw(key, data);
61
+ return { cached: false, data };
62
+ }
63
+ function proxyURL({ value, hashString, collect }) {
64
+ const type = extractFontType(value);
65
+ const hash = `${hashString(value)}.${type}`;
66
+ const url = collect({ hash, type, value });
67
+ return url;
68
+ }
69
+ function isGenericFontFamily(str) {
70
+ return Object.keys(DEFAULT_FALLBACKS).includes(str);
71
+ }
72
+ async function generateFallbacksCSS({
73
+ family,
74
+ fallbacks: _fallbacks,
75
+ font: fontData,
76
+ metrics
77
+ }) {
78
+ let fallbacks = [..._fallbacks];
79
+ if (fallbacks.length === 0) {
80
+ return null;
81
+ }
82
+ let css = "";
83
+ if (!metrics) {
84
+ return { css, fallbacks };
85
+ }
86
+ const lastFallback = fallbacks[fallbacks.length - 1];
87
+ if (!isGenericFontFamily(lastFallback)) {
88
+ return { css, fallbacks };
89
+ }
90
+ const localFonts = DEFAULT_FALLBACKS[lastFallback];
91
+ if (localFonts.length === 0) {
92
+ return { css, fallbacks };
93
+ }
94
+ const foundMetrics = await metrics.getMetricsForFamily(family.name, fontData);
95
+ if (!foundMetrics) {
96
+ return { css, fallbacks };
97
+ }
98
+ const localFontsMappings = localFonts.map((font) => ({
99
+ font,
100
+ name: `"${family.nameWithHash} fallback: ${font}"`
101
+ }));
102
+ fallbacks = [.../* @__PURE__ */ new Set([...localFontsMappings.map((m) => m.name), ...fallbacks])];
103
+ for (const { font, name } of localFontsMappings) {
104
+ css += metrics.generateFontFace(foundMetrics, { font, name });
105
+ }
106
+ return { css, fallbacks };
107
+ }
108
+ function dedupe(arr) {
109
+ return [...new Set(arr)];
110
+ }
111
+ function resolveVariants({
112
+ variants,
113
+ root
114
+ }) {
115
+ return variants.map((variant) => ({
116
+ ...variant,
117
+ weight: variant.weight.toString(),
118
+ src: variant.src.map((value) => {
119
+ const isValue = typeof value === "string" || value instanceof URL;
120
+ const url = (isValue ? value : value.url).toString();
121
+ const tech = isValue ? void 0 : value.tech;
122
+ return {
123
+ url: fileURLToPath(resolveEntrypoint(root, url)),
124
+ tech
125
+ };
126
+ })
127
+ }));
128
+ }
129
+ async function resolveFontFamily({
130
+ family,
131
+ generateNameWithHash,
132
+ root,
133
+ resolveMod
134
+ }) {
135
+ const nameWithHash = generateNameWithHash(family);
136
+ if (family.provider === LOCAL_PROVIDER_NAME) {
137
+ return {
138
+ ...family,
139
+ nameWithHash,
140
+ variants: resolveVariants({ variants: family.variants, root }),
141
+ fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0
142
+ };
143
+ }
144
+ return {
145
+ ...family,
146
+ nameWithHash,
147
+ provider: await resolveProvider({
148
+ root,
149
+ resolveMod,
150
+ provider: family.provider
151
+ }),
152
+ weights: family.weights ? dedupe(family.weights.map((weight) => weight.toString())) : void 0,
153
+ styles: family.styles ? dedupe(family.styles) : void 0,
154
+ subsets: family.subsets ? dedupe(family.subsets) : void 0,
155
+ fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0,
156
+ unicodeRange: family.unicodeRange ? dedupe(family.unicodeRange) : void 0
157
+ };
158
+ }
159
+ function sortObjectByKey(unordered) {
160
+ const ordered = Object.keys(unordered).sort().reduce((obj, key) => {
161
+ obj[key] = unordered[key];
162
+ return obj;
163
+ }, {});
164
+ return ordered;
165
+ }
166
+ function familiesToUnifontProviders({
167
+ families,
168
+ hashString
169
+ }) {
170
+ const hashes = /* @__PURE__ */ new Set();
171
+ const providers = [];
172
+ for (const { provider } of families) {
173
+ if (provider === LOCAL_PROVIDER_NAME) {
174
+ continue;
175
+ }
176
+ const unifontProvider = provider.provider(provider.config);
177
+ const hash = hashString(
178
+ JSON.stringify(
179
+ sortObjectByKey({
180
+ name: unifontProvider._name,
181
+ ...provider.config
182
+ })
183
+ )
184
+ );
185
+ if (hashes.has(hash)) {
186
+ continue;
187
+ }
188
+ unifontProvider._name += `-${hash}`;
189
+ provider.name = unifontProvider._name;
190
+ hashes.add(hash);
191
+ providers.push(unifontProvider);
192
+ }
193
+ return { families, providers };
194
+ }
195
+ function resolveEntrypoint(root, entrypoint) {
196
+ const require2 = createRequire(root);
197
+ try {
198
+ return pathToFileURL(require2.resolve(entrypoint));
199
+ } catch {
200
+ return new URL(entrypoint, root);
201
+ }
202
+ }
203
+ export {
204
+ cache,
205
+ extractFontType,
206
+ familiesToUnifontProviders,
207
+ generateFallbacksCSS,
208
+ generateFontFace,
209
+ isFontType,
210
+ isGenericFontFamily,
211
+ proxyURL,
212
+ resolveEntrypoint,
213
+ resolveFontFamily,
214
+ sortObjectByKey
215
+ };
@@ -0,0 +1,10 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { Logger } from '../../core/logger/core.js';
3
+ import type { AstroSettings } from '../../types/astro.js';
4
+ interface Options {
5
+ settings: AstroSettings;
6
+ sync: boolean;
7
+ logger: Logger;
8
+ }
9
+ export declare function fontsPlugin({ settings, sync, logger }: Options): Plugin;
10
+ export {};
@@ -0,0 +1,217 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { isAbsolute } from "node:path";
4
+ import { fileURLToPath } from "node:url";
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
+ import { collectErrorMetadata } from "../../core/errors/dev/utils.js";
10
+ import { AstroError, AstroErrorData, isAstroError } from "../../core/errors/index.js";
11
+ import { formatErrorMessage } from "../../core/messages.js";
12
+ import { getClientOutputDirectory } from "../../prerender/utils.js";
13
+ import {
14
+ CACHE_DIR,
15
+ RESOLVED_VIRTUAL_MODULE_ID,
16
+ URL_PREFIX,
17
+ VIRTUAL_MODULE_ID
18
+ } from "./constants.js";
19
+ import { loadFonts } from "./load.js";
20
+ import { generateFallbackFontFace, getMetricsForFamily, readMetrics } from "./metrics.js";
21
+ import { cache, extractFontType, resolveFontFamily, sortObjectByKey } from "./utils.js";
22
+ async function fetchFont(url) {
23
+ try {
24
+ if (isAbsolute(url)) {
25
+ return await readFile(url);
26
+ }
27
+ const response = await fetch(url);
28
+ if (!response.ok) {
29
+ throw new Error(`Response was not successful, received status code ${response.status}`);
30
+ }
31
+ return Buffer.from(await response.arrayBuffer());
32
+ } catch (cause) {
33
+ throw new AstroError(
34
+ {
35
+ ...AstroErrorData.CannotFetchFontFile,
36
+ message: AstroErrorData.CannotFetchFontFile.message(url)
37
+ },
38
+ { cause }
39
+ );
40
+ }
41
+ }
42
+ function fontsPlugin({ settings, sync, logger }) {
43
+ if (!settings.config.experimental.fonts) {
44
+ return {
45
+ name: "astro:fonts:fallback",
46
+ config() {
47
+ return {
48
+ build: {
49
+ rollupOptions: {
50
+ external: [VIRTUAL_MODULE_ID]
51
+ }
52
+ }
53
+ };
54
+ }
55
+ };
56
+ }
57
+ const baseUrl = removeTrailingForwardSlash(settings.config.base) + URL_PREFIX;
58
+ let resolvedMap = null;
59
+ let hashToUrlMap = null;
60
+ let isBuild;
61
+ let storage = null;
62
+ const cleanup = () => {
63
+ resolvedMap = null;
64
+ hashToUrlMap = null;
65
+ storage = null;
66
+ };
67
+ async function initialize({ resolveMod, base }) {
68
+ const { h64ToString } = await xxhash();
69
+ storage = createStorage({
70
+ // Types are weirly exported
71
+ driver: fsLiteDriver({
72
+ base: fileURLToPath(base)
73
+ })
74
+ });
75
+ hashToUrlMap = /* @__PURE__ */ new Map();
76
+ resolvedMap = /* @__PURE__ */ new Map();
77
+ const families = [];
78
+ for (const family of settings.config.experimental.fonts) {
79
+ families.push(
80
+ await resolveFontFamily({
81
+ family,
82
+ root: settings.config.root,
83
+ resolveMod,
84
+ generateNameWithHash: (_family) => `${_family.name}-${h64ToString(JSON.stringify(sortObjectByKey(_family)))}`
85
+ })
86
+ );
87
+ }
88
+ await loadFonts({
89
+ base: baseUrl,
90
+ families,
91
+ storage,
92
+ hashToUrlMap,
93
+ resolvedMap,
94
+ hashString: h64ToString,
95
+ generateFallbackFontFace,
96
+ getMetricsForFamily: async (name, font) => {
97
+ let metrics = await getMetricsForFamily(name);
98
+ if (font && !metrics) {
99
+ const { data } = await cache(storage, font.hash, () => fetchFont(font.url));
100
+ metrics = await readMetrics(name, data);
101
+ }
102
+ return metrics;
103
+ },
104
+ log: (message) => logger.info("assets", message)
105
+ });
106
+ }
107
+ return {
108
+ name: "astro:fonts",
109
+ config(_, { command }) {
110
+ isBuild = command === "build";
111
+ },
112
+ async buildStart() {
113
+ if (isBuild) {
114
+ await initialize({
115
+ resolveMod: (id) => import(id),
116
+ base: new URL(CACHE_DIR, settings.config.cacheDir)
117
+ });
118
+ }
119
+ },
120
+ async configureServer(server) {
121
+ await initialize({
122
+ resolveMod: (id) => server.ssrLoadModule(id),
123
+ // In dev, we cache fonts data in .astro so it can be easily inspected and cleared
124
+ base: new URL(CACHE_DIR, settings.dotAstroDir)
125
+ });
126
+ const localPaths = [...hashToUrlMap.values()].filter((url) => isAbsolute(url));
127
+ server.watcher.on("change", (path) => {
128
+ if (localPaths.includes(path)) {
129
+ logger.info("assets", "Font file updated");
130
+ server.restart();
131
+ }
132
+ });
133
+ server.watcher.on("unlink", (path) => {
134
+ if (localPaths.includes(path)) {
135
+ logger.warn(
136
+ "assets",
137
+ `The font file ${JSON.stringify(path)} referenced in your config has been deleted. Restore the file or remove this font from your configuration if it is no longer needed.`
138
+ );
139
+ }
140
+ });
141
+ server.middlewares.use(URL_PREFIX, async (req, res, next) => {
142
+ if (!req.url) {
143
+ return next();
144
+ }
145
+ const hash = req.url.slice(1);
146
+ const url = hashToUrlMap?.get(hash);
147
+ if (!url) {
148
+ return next();
149
+ }
150
+ res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
151
+ res.setHeader("Pragma", "no-cache");
152
+ res.setHeader("Expires", 0);
153
+ try {
154
+ const { data } = await cache(storage, hash, () => fetchFont(url));
155
+ res.setHeader("Content-Length", data.length);
156
+ res.setHeader("Content-Type", `font/${extractFontType(hash)}`);
157
+ res.end(data);
158
+ } catch (err) {
159
+ logger.error("assets", "Cannot download font file");
160
+ if (isAstroError(err)) {
161
+ logger.error(
162
+ "SKIP_FORMAT",
163
+ formatErrorMessage(collectErrorMetadata(err), logger.level() === "debug")
164
+ );
165
+ }
166
+ res.statusCode = 500;
167
+ res.end();
168
+ }
169
+ });
170
+ },
171
+ resolveId(id) {
172
+ if (id === VIRTUAL_MODULE_ID) {
173
+ return RESOLVED_VIRTUAL_MODULE_ID;
174
+ }
175
+ },
176
+ load(id, opts) {
177
+ if (id === RESOLVED_VIRTUAL_MODULE_ID && opts?.ssr) {
178
+ return {
179
+ code: `export const fontsData = new Map(${JSON.stringify(Array.from(resolvedMap?.entries() ?? []))})`
180
+ };
181
+ }
182
+ },
183
+ async buildEnd() {
184
+ if (sync || settings.config.experimental.fonts.length === 0) {
185
+ cleanup();
186
+ return;
187
+ }
188
+ try {
189
+ const dir = getClientOutputDirectory(settings);
190
+ const fontsDir = new URL("." + baseUrl, dir);
191
+ try {
192
+ mkdirSync(fontsDir, { recursive: true });
193
+ } catch (cause) {
194
+ throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause });
195
+ }
196
+ if (hashToUrlMap) {
197
+ logger.info("assets", "Copying fonts...");
198
+ await Promise.all(
199
+ Array.from(hashToUrlMap.entries()).map(async ([hash, url]) => {
200
+ const { data } = await cache(storage, hash, () => fetchFont(url));
201
+ try {
202
+ writeFileSync(new URL(hash, fontsDir), data);
203
+ } catch (cause) {
204
+ throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause });
205
+ }
206
+ })
207
+ );
208
+ }
209
+ } finally {
210
+ cleanup();
211
+ }
212
+ }
213
+ };
214
+ }
215
+ export {
216
+ fontsPlugin
217
+ };
@@ -4,7 +4,11 @@
4
4
  *
5
5
  * If some functions don't need to be exposed, just import the file that contains the functions.
6
6
  */
7
- export { emitESMImage } from './node/emitAsset.js';
7
+ export {
8
+ /**
9
+ * @deprecated
10
+ */
11
+ emitESMImage, emitImageMetadata, } from './node/emitAsset.js';
8
12
  export { isESMImportedImage, isRemoteImage } from './imageKind.js';
9
13
  export { imageMetadata } from './metadata.js';
10
14
  export { getOrigQueryParams } from './queryParams.js';
@@ -1,4 +1,7 @@
1
- import { emitESMImage } from "./node/emitAsset.js";
1
+ import {
2
+ emitESMImage,
3
+ emitImageMetadata
4
+ } from "./node/emitAsset.js";
2
5
  import { isESMImportedImage, isRemoteImage } from "./imageKind.js";
3
6
  import { imageMetadata } from "./metadata.js";
4
7
  import { getOrigQueryParams } from "./queryParams.js";
@@ -14,6 +17,7 @@ import {
14
17
  } from "./remotePattern.js";
15
18
  export {
16
19
  emitESMImage,
20
+ emitImageMetadata,
17
21
  getOrigQueryParams,
18
22
  hashTransform,
19
23
  imageMetadata,
@@ -9,11 +9,21 @@ type ImageMetadataWithContents = ImageMetadata & {
9
9
  *
10
10
  * @param {string | undefined} id - The identifier or path of the image file to process. If undefined, the function returns immediately.
11
11
  * @param {boolean} _watchMode - **Deprecated**: Indicates if the method is operating in watch mode. This parameter will be removed or updated in the future.
12
- * @param {boolean} experimentalSvgEnabled - A flag to enable experimental handling of SVG files. Embeds SVG file data if set to true.
12
+ * @param {boolean} _experimentalSvgEnabled - **Deprecated**: A flag to enable experimental handling of SVG files. Embeds SVG file data if set to true.
13
13
  * @param {FileEmitter | undefined} [fileEmitter] - Function for emitting files during the build process. May throw in certain scenarios.
14
14
  * @return {Promise<ImageMetadataWithContents | undefined>} Resolves to metadata with optional image contents or `undefined` if processing fails.
15
15
  */
16
16
  export declare function emitESMImage(id: string | undefined,
17
17
  /** @deprecated */
18
- _watchMode: boolean, experimentalSvgEnabled: boolean, fileEmitter?: FileEmitter): Promise<ImageMetadataWithContents | undefined>;
18
+ _watchMode: boolean,
19
+ /** @deprecated */
20
+ _experimentalSvgEnabled: boolean, fileEmitter?: FileEmitter): Promise<ImageMetadataWithContents | undefined>;
21
+ /**
22
+ * Processes an image file and emits its metadata and optionally its contents. This function supports both build and development modes.
23
+ *
24
+ * @param {string | undefined} id - The identifier or path of the image file to process. If undefined, the function returns immediately.
25
+ * @param {FileEmitter | undefined} [fileEmitter] - Function for emitting files during the build process. May throw in certain scenarios.
26
+ * @return {Promise<ImageMetadataWithContents | undefined>} Resolves to metadata with optional image contents or `undefined` if processing fails.
27
+ */
28
+ export declare function emitImageMetadata(id: string | undefined, fileEmitter?: FileEmitter): Promise<ImageMetadataWithContents | undefined>;
19
29
  export {};
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import { prependForwardSlash, slash } from "../../../core/path.js";
5
5
  import { imageMetadata } from "../metadata.js";
6
- async function emitESMImage(id, _watchMode, experimentalSvgEnabled, fileEmitter) {
6
+ async function emitESMImage(id, _watchMode, _experimentalSvgEnabled, fileEmitter) {
7
7
  if (!id) {
8
8
  return void 0;
9
9
  }
@@ -24,9 +24,50 @@ async function emitESMImage(id, _watchMode, experimentalSvgEnabled, fileEmitter)
24
24
  writable: false,
25
25
  value: id
26
26
  });
27
- if (fileMetadata.format === "svg" && experimentalSvgEnabled) {
28
- emittedImage.contents = fileData;
27
+ let isBuild = typeof fileEmitter === "function";
28
+ if (isBuild) {
29
+ const pathname = decodeURI(url.pathname);
30
+ const filename = path.basename(pathname, path.extname(pathname) + `.${fileMetadata.format}`);
31
+ try {
32
+ const handle = fileEmitter({
33
+ name: filename,
34
+ source: await fs.readFile(url),
35
+ type: "asset"
36
+ });
37
+ emittedImage.src = `__ASTRO_ASSET_IMAGE__${handle}__`;
38
+ } catch {
39
+ isBuild = false;
40
+ }
41
+ }
42
+ if (!isBuild) {
43
+ url.searchParams.append("origWidth", fileMetadata.width.toString());
44
+ url.searchParams.append("origHeight", fileMetadata.height.toString());
45
+ url.searchParams.append("origFormat", fileMetadata.format);
46
+ emittedImage.src = `/@fs` + prependForwardSlash(fileURLToNormalizedPath(url));
47
+ }
48
+ return emittedImage;
49
+ }
50
+ async function emitImageMetadata(id, fileEmitter) {
51
+ if (!id) {
52
+ return void 0;
53
+ }
54
+ const url = pathToFileURL(id);
55
+ let fileData;
56
+ try {
57
+ fileData = await fs.readFile(url);
58
+ } catch {
59
+ return void 0;
29
60
  }
61
+ const fileMetadata = await imageMetadata(fileData, id);
62
+ const emittedImage = {
63
+ src: "",
64
+ ...fileMetadata
65
+ };
66
+ Object.defineProperty(emittedImage, "fsPath", {
67
+ enumerable: false,
68
+ writable: false,
69
+ value: id
70
+ });
30
71
  let isBuild = typeof fileEmitter === "function";
31
72
  if (isBuild) {
32
73
  const pathname = decodeURI(url.pathname);
@@ -54,5 +95,6 @@ function fileURLToNormalizedPath(filePath) {
54
95
  return slash(fileURLToPath(filePath) + filePath.search).replace(/\\/g, "/");
55
96
  }
56
97
  export {
57
- emitESMImage
98
+ emitESMImage,
99
+ emitImageMetadata
58
100
  };
@@ -1,3 +1,3 @@
1
- export declare const typeHandlers: Map<"svg" | "jpg" | "png" | "tiff" | "webp" | "gif" | "heif" | "icns" | "ktx" | "bmp" | "cur" | "dds" | "ico" | "j2c" | "jp2" | "pnm" | "psd" | "tga", import("./interface.js").IImage>;
2
- export declare const types: ("svg" | "jpg" | "png" | "tiff" | "webp" | "gif" | "heif" | "icns" | "ktx" | "bmp" | "cur" | "dds" | "ico" | "j2c" | "jp2" | "pnm" | "psd" | "tga")[];
1
+ export declare const typeHandlers: Map<"jpg" | "png" | "tiff" | "webp" | "gif" | "svg" | "heif" | "icns" | "ktx" | "bmp" | "cur" | "dds" | "ico" | "j2c" | "jp2" | "pnm" | "psd" | "tga", import("./interface.js").IImage>;
2
+ export declare const types: ("jpg" | "png" | "tiff" | "webp" | "gif" | "svg" | "heif" | "icns" | "ktx" | "bmp" | "cur" | "dds" | "ico" | "j2c" | "jp2" | "pnm" | "psd" | "tga")[];
3
3
  export type imageType = typeof types[number];
@@ -1,5 +1,12 @@
1
+ import type * as fsMod from 'node:fs';
1
2
  import type * as vite from 'vite';
3
+ import type { Logger } from '../core/logger/core.js';
2
4
  import type { AstroSettings } from '../types/astro.js';
3
- export default function assets({ settings }: {
5
+ interface Options {
4
6
  settings: AstroSettings;
5
- }): vite.Plugin[];
7
+ sync: boolean;
8
+ logger: Logger;
9
+ fs: typeof fsMod;
10
+ }
11
+ export default function assets({ fs, settings, sync, logger }: Options): vite.Plugin[];
12
+ export {};
@@ -10,6 +10,7 @@ import {
10
10
  } from "../core/path.js";
11
11
  import { normalizePath } from "../core/viteUtils.js";
12
12
  import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from "./consts.js";
13
+ import { fontsPlugin } from "./fonts/vite-plugin-fonts.js";
13
14
  import { getAssetsPrefix } from "./utils/getAssetsPrefix.js";
14
15
  import { isESMImportedImage } from "./utils/imageKind.js";
15
16
  import { emitESMImage } from "./utils/node/emitAsset.js";
@@ -63,7 +64,7 @@ const addStaticImageFactory = (settings) => {
63
64
  }
64
65
  };
65
66
  };
66
- function assets({ settings }) {
67
+ function assets({ fs, settings, sync, logger }) {
67
68
  let resolvedConfig;
68
69
  let shouldEmitFile = false;
69
70
  let isBuild = false;
@@ -94,6 +95,7 @@ function assets({ settings }) {
94
95
  import { getImage as getImageInternal } from "astro/assets";
95
96
  export { default as Image } from "astro/components/${imageComponentPrefix}Image.astro";
96
97
  export { default as Picture } from "astro/components/${imageComponentPrefix}Picture.astro";
98
+ export { default as Font } from "astro/components/Font.astro";
97
99
  export { inferRemoteSize } from "astro/assets/utils/inferRemoteSize.js";
98
100
 
99
101
  export const imageConfig = ${JSON.stringify({ ...settings.config.image, experimentalResponsiveImages: settings.config.experimental.responsiveImages })};
@@ -170,7 +172,7 @@ function assets({ settings }) {
170
172
  const imageMetadata = await emitESMImage(
171
173
  id,
172
174
  this.meta.watchMode,
173
- !!settings.config.experimental.svg,
175
+ id.endsWith(".svg"),
174
176
  emitFile
175
177
  );
176
178
  if (!imageMetadata) {
@@ -179,9 +181,9 @@ function assets({ settings }) {
179
181
  message: AstroErrorData.ImageNotFound.message(id)
180
182
  });
181
183
  }
182
- if (settings.config.experimental.svg && /\.svg$/.test(id)) {
183
- const { contents, ...metadata } = imageMetadata;
184
- return { code: makeSvgComponent(metadata, contents) };
184
+ if (id.endsWith(".svg")) {
185
+ const contents = await fs.promises.readFile(imageMetadata.fsPath, { encoding: "utf8" });
186
+ return { code: makeSvgComponent(imageMetadata, contents) };
185
187
  }
186
188
  if (options?.ssr) {
187
189
  return {
@@ -198,7 +200,8 @@ function assets({ settings }) {
198
200
  }
199
201
  }
200
202
  }
201
- }
203
+ },
204
+ fontsPlugin({ settings, sync, logger })
202
205
  ];
203
206
  }
204
207
  export {
@@ -545,7 +545,9 @@ async function tryToInstallIntegrations({
545
545
  }).filter(Boolean).flat();
546
546
  const installCommand = resolveCommand(packageManager?.agent ?? "npm", "add", inheritedFlags);
547
547
  if (!installCommand) return 0 /* none */;
548
- const installSpecifiers = await convertIntegrationsToInstallSpecifiers(integrations);
548
+ const installSpecifiers = await convertIntegrationsToInstallSpecifiers(integrations).then(
549
+ (specifiers) => installCommand.command === "deno" ? specifiers.map((specifier) => `npm:${specifier}`) : specifiers
550
+ );
549
551
  const coloredOutput = `${bold(installCommand.command)} ${installCommand.args.join(" ")} ${cyan(installSpecifiers.join(" "))}`;
550
552
  const message = `
551
553
  ${boxen(coloredOutput, {
@@ -43,6 +43,9 @@ async function installPackage(packageNames, options, logger) {
43
43
  });
44
44
  const installCommand = resolveCommand(packageManager?.agent ?? "npm", "add", []);
45
45
  if (!installCommand) return false;
46
+ if (installCommand.command === "deno") {
47
+ packageNames = packageNames.map((name) => `npm:${name}`);
48
+ }
46
49
  const coloredOutput = `${bold(installCommand.command)} ${installCommand.args.join(" ")} ${cyan(packageNames.join(" "))}`;
47
50
  const message = `
48
51
  ${boxen(coloredOutput, {
@@ -4,6 +4,8 @@ export { defineConfig, getViteConfig } from './index.js';
4
4
  export { envField } from '../env/config.js';
5
5
  export { mergeConfig } from '../core/config/merge.js';
6
6
  export { validateConfig } from '../core/config/validate.js';
7
+ export { fontProviders, defineAstroFontProvider } from '../assets/fonts/providers/index.js';
8
+ export type { AstroFontProvider as FontProvider } from '../assets/fonts/types.js';
7
9
  /**
8
10
  * Return the configuration needed to use the Sharp-based image service
9
11
  */