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.
- package/client.d.ts +3 -0
- package/components/Font.astro +32 -0
- package/dist/actions/plugins.js +2 -2
- package/dist/assets/fonts/config.d.ts +378 -0
- package/dist/assets/fonts/config.js +157 -0
- package/dist/assets/fonts/constants.d.ts +15 -0
- package/dist/assets/fonts/constants.js +41 -0
- package/dist/assets/fonts/load.d.ts +20 -0
- package/dist/assets/fonts/load.js +133 -0
- package/dist/assets/fonts/metrics.d.ts +10 -0
- package/dist/assets/fonts/metrics.js +84 -0
- package/dist/assets/fonts/providers/entrypoints/adobe.d.ts +2 -0
- package/dist/assets/fonts/providers/entrypoints/adobe.js +5 -0
- package/dist/assets/fonts/providers/entrypoints/bunny.d.ts +1 -0
- package/dist/assets/fonts/providers/entrypoints/bunny.js +5 -0
- package/dist/assets/fonts/providers/entrypoints/fontshare.d.ts +1 -0
- package/dist/assets/fonts/providers/entrypoints/fontshare.js +5 -0
- package/dist/assets/fonts/providers/entrypoints/fontsource.d.ts +1 -0
- package/dist/assets/fonts/providers/entrypoints/fontsource.js +5 -0
- package/dist/assets/fonts/providers/entrypoints/google.d.ts +2 -0
- package/dist/assets/fonts/providers/entrypoints/google.js +5 -0
- package/dist/assets/fonts/providers/index.d.ts +48 -0
- package/dist/assets/fonts/providers/index.js +40 -0
- package/dist/assets/fonts/providers/local.d.ts +10 -0
- package/dist/assets/fonts/providers/local.js +30 -0
- package/dist/assets/fonts/providers/utils.d.ts +9 -0
- package/dist/assets/fonts/providers/utils.js +37 -0
- package/dist/assets/fonts/sync.d.ts +2 -0
- package/dist/assets/fonts/sync.js +17 -0
- package/dist/assets/fonts/types.d.ts +45 -0
- package/dist/assets/fonts/types.js +0 -0
- package/dist/assets/fonts/utils.d.ts +95 -0
- package/dist/assets/fonts/utils.js +215 -0
- package/dist/assets/fonts/vite-plugin-fonts.d.ts +10 -0
- package/dist/assets/fonts/vite-plugin-fonts.js +217 -0
- package/dist/assets/utils/index.d.ts +5 -1
- package/dist/assets/utils/index.js +5 -1
- package/dist/assets/utils/node/emitAsset.d.ts +12 -2
- package/dist/assets/utils/node/emitAsset.js +46 -4
- package/dist/assets/utils/vendor/image-size/types/index.d.ts +2 -2
- package/dist/assets/vite-plugin-assets.d.ts +9 -2
- package/dist/assets/vite-plugin-assets.js +9 -6
- package/dist/cli/add/index.js +3 -1
- package/dist/cli/install-package.js +3 -0
- package/dist/config/entrypoint.d.ts +2 -0
- package/dist/config/entrypoint.js +3 -0
- package/dist/config/index.d.ts +2 -1
- package/dist/content/content-layer.js +5 -4
- package/dist/content/runtime-assets.js +1 -0
- package/dist/content/utils.d.ts +10 -10
- package/dist/content/vite-plugin-content-imports.js +4 -2
- package/dist/core/build/generate.js +2 -2
- package/dist/core/build/pipeline.js +2 -2
- package/dist/core/build/plugins/plugin-prerender.js +0 -3
- package/dist/core/build/static-build.js +2 -2
- package/dist/core/config/schemas/base.d.ts +446 -49
- package/dist/core/config/schemas/base.js +3 -7
- package/dist/core/config/schemas/refined.js +12 -0
- package/dist/core/config/schemas/relative.d.ts +581 -77
- package/dist/core/config/schemas/relative.js +1 -2
- package/dist/core/constants.js +1 -1
- package/dist/core/create-vite.js +2 -2
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/errors-data.d.ts +82 -26
- package/dist/core/errors/errors-data.js +45 -16
- package/dist/core/logger/core.d.ts +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/core/middleware/vite-plugin.js +2 -2
- package/dist/core/render-context.js +39 -5
- package/dist/core/routing/rewrite.js +14 -5
- package/dist/core/session.d.ts +2 -4
- package/dist/core/session.js +4 -29
- package/dist/core/sync/index.js +2 -0
- package/dist/env/schema.d.ts +6 -6
- package/dist/integrations/hooks.js +2 -3
- package/dist/manifest/virtual-module.d.ts +1 -5
- package/dist/manifest/virtual-module.js +1 -18
- package/dist/prerender/utils.d.ts +5 -1
- package/dist/prerender/utils.js +8 -8
- package/dist/runtime/client/dev-toolbar/apps/audit/annotations.d.ts +6 -0
- package/dist/runtime/client/dev-toolbar/apps/audit/annotations.js +27 -0
- package/dist/runtime/client/dev-toolbar/apps/audit/index.js +10 -4
- package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.js +2 -2
- package/dist/runtime/server/render/common.js +8 -0
- package/dist/runtime/server/render/instruction.d.ts +5 -5
- package/dist/runtime/server/render/server-islands.d.ts +1 -0
- package/dist/runtime/server/render/server-islands.js +29 -31
- package/dist/transitions/swap-functions.d.ts +1 -1
- package/dist/transitions/swap-functions.js +7 -6
- package/dist/types/public/config.d.ts +155 -98
- package/dist/types/public/internal.d.ts +1 -0
- package/dist/vite-plugin-astro-server/plugin.js +1 -1
- package/package.json +9 -5
- 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 {
|
|
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 {
|
|
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}
|
|
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,
|
|
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,
|
|
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
|
-
|
|
28
|
-
|
|
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<"
|
|
2
|
-
export declare const types: ("
|
|
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
|
-
|
|
5
|
+
interface Options {
|
|
4
6
|
settings: AstroSettings;
|
|
5
|
-
|
|
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
|
-
|
|
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 (
|
|
183
|
-
const
|
|
184
|
-
return { code: makeSvgComponent(
|
|
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 {
|
package/dist/cli/add/index.js
CHANGED
|
@@ -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
|
*/
|