full site update

This commit is contained in:
2025-07-24 18:46:24 +02:00
parent bfe2b90d8d
commit 37a6e0ab31
6912 changed files with 540482 additions and 361712 deletions

66
node_modules/astro/astro-jsx.d.ts generated vendored
View File

@@ -16,23 +16,21 @@ declare namespace astroHTML.JSX {
children: {};
}
interface IntrinsicAttributes
extends AstroBuiltinProps,
AstroBuiltinAttributes,
AstroClientDirectives {
interface IntrinsicAttributes extends AstroComponentDirectives, AstroBuiltinAttributes {
slot?: string | undefined | null;
children?: Children;
}
type AstroBuiltinProps = import('./dist/@types/astro.js').AstroBuiltinProps;
type AstroClientDirectives = import('./dist/@types/astro.js').AstroClientDirectives;
type AstroBuiltinAttributes = import('./dist/@types/astro.js').AstroBuiltinAttributes;
type AstroDefineVarsAttribute = import('./dist/@types/astro.js').AstroDefineVarsAttribute;
type AstroScriptAttributes = import('./dist/@types/astro.js').AstroScriptAttributes &
// biome-ignore format: bug
type AstroComponentDirectives = import('./dist/types/public/elements.js').AstroComponentDirectives;
type AstroBuiltinAttributes = import('./dist/types/public/elements.js').AstroBuiltinAttributes;
// biome-ignore format: bug
type AstroDefineVarsAttribute = import('./dist/types/public/elements.js').AstroDefineVarsAttribute;
type AstroScriptAttributes = import('./dist/types/public/elements.js').AstroScriptAttributes &
AstroDefineVarsAttribute;
type AstroStyleAttributes = import('./dist/@types/astro.js').AstroStyleAttributes &
type AstroStyleAttributes = import('./dist/types/public/elements.js').AstroStyleAttributes &
AstroDefineVarsAttribute;
type AstroSlotAttributes = import('./dist/@types/astro.js').AstroSlotAttributes;
type AstroSlotAttributes = import('./dist/types/public/elements.js').AstroSlotAttributes;
// This is an unfortunate use of `any`, but unfortunately we can't make a type that works for every framework
// without importing every single framework's types (which comes with its own set of problems).
@@ -176,15 +174,15 @@ declare namespace astroHTML.JSX {
onfullscreenerror?: string | undefined | null;
}
// All the WAI-ARIA 1.1 attributes from https://www.w3.org/TR/wai-aria-1.1/
// All the WAI-ARIA 1.2 attributes from https://www.w3.org/TR/wai-aria-1.2/#state_prop_def
interface AriaAttributes {
/** Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application. */
/** Identifies the currently active element when DOM focus is on a composite widget, combobox, textbox, group, or application. */
'aria-activedescendant'?: string | undefined | null;
/** Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute. */
'aria-atomic'?: boolean | 'false' | 'true' | undefined | null;
/**
* Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be
* presented if they are made.
* Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for a combobox, searchbox, or textbox and specifies
* how predictions would be presented if they were made.
*/
'aria-autocomplete'?: 'none' | 'inline' | 'list' | 'both' | undefined | null;
/** Indicates an element is being modified and that assistive technologies MAY want to wait until the modifications are complete before exposing them to the user. */
@@ -247,11 +245,11 @@ declare namespace astroHTML.JSX {
*/
'aria-dropeffect'?: 'none' | 'copy' | 'execute' | 'link' | 'move' | 'popup' | undefined | null;
/**
* Identifies the element that provides an error message for the object.
* Identifies the element that provides an error message for an object.
* @see aria-invalid @see aria-describedby.
*/
'aria-errormessage'?: string | undefined | null;
/** Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed. */
/** Indicates whether a grouping element owned or controlled by this element is expanded or collapsed. */
'aria-expanded'?: boolean | 'false' | 'true' | undefined | null;
/**
* Identifies the next element (or elements) in an alternate reading order of content which, at the user's discretion,
@@ -396,32 +394,43 @@ declare namespace astroHTML.JSX {
'aria-valuetext'?: string | undefined | null;
}
// All the WAI-ARIA 1.1 role attribute values from https://www.w3.org/TR/wai-aria-1.1/#role_definitions
// All the WAI-ARIA 1.2 role attribute values from https://www.w3.org/TR/wai-aria-1.2/#role_definitions
type AriaRole =
| 'alert'
| 'alertdialog'
| 'application'
| 'article'
| 'banner'
| 'blockquote'
| 'button'
| 'caption'
| 'cell'
| 'checkbox'
| 'code'
| 'columnheader'
| 'combobox'
| 'command'
| 'complementary'
| 'composite'
| 'contentinfo'
| 'definition'
| 'deletion'
| 'dialog'
| 'directory'
| 'document'
| 'emphasis'
| 'feed'
| 'figure'
| 'form'
| 'generic'
| 'grid'
| 'gridcell'
| 'group'
| 'heading'
| 'img'
| 'input'
| 'insertion'
| 'landmark'
| 'link'
| 'list'
| 'listbox'
@@ -430,6 +439,7 @@ declare namespace astroHTML.JSX {
| 'main'
| 'marquee'
| 'math'
| 'meter'
| 'menu'
| 'menubar'
| 'menuitem'
@@ -439,21 +449,31 @@ declare namespace astroHTML.JSX {
| 'none'
| 'note'
| 'option'
| 'paragraph'
| 'presentation'
| 'progressbar'
| 'radio'
| 'radiogroup'
| 'range'
| 'region'
| 'roletype'
| 'row'
| 'rowgroup'
| 'rowheader'
| 'scrollbar'
| 'search'
| 'searchbox'
| 'section'
| 'sectionhead'
| 'select'
| 'separator'
| 'slider'
| 'spinbutton'
| 'status'
| 'strong'
| 'structure'
| 'subscript'
| 'superscript'
| 'switch'
| 'tab'
| 'table'
@@ -461,12 +481,15 @@ declare namespace astroHTML.JSX {
| 'tabpanel'
| 'term'
| 'textbox'
| 'time'
| 'timer'
| 'toolbar'
| 'tooltip'
| 'tree'
| 'treegrid'
| 'treeitem';
| 'treeitem'
| 'widget'
| 'window';
type CssProperty = keyof Omit<
CSSStyleDeclaration,
@@ -681,6 +704,7 @@ declare namespace astroHTML.JSX {
interface DialogHTMLAttributes extends HTMLAttributes {
open?: boolean | string | undefined | null;
closedby?: 'none' | 'closerequest' | 'any' | undefined | null;
}
interface EmbedHTMLAttributes extends HTMLAttributes {
@@ -838,10 +862,12 @@ declare namespace astroHTML.JSX {
interface LinkHTMLAttributes extends HTMLAttributes {
as?: string | undefined | null;
blocking?: 'render' | undefined | null;
crossorigin?: boolean | string | undefined | null;
disabled?: boolean | undefined | null;
fetchpriority?: 'auto' | 'high' | 'low' | undefined | null;
href?: string | URL | undefined | null;
hreflang?: string | undefined | null;
fetchpriority?: 'auto' | 'high' | 'low' | undefined | null;
integrity?: string | undefined | null;
media?: string | undefined | null;
imagesrcset?: string | undefined | null;

2
node_modules/astro/astro.js generated vendored
View File

@@ -12,7 +12,7 @@ const CI_INSTRUCTIONS = {
};
// Hardcode supported Node.js version so we don't have to read differently in CJS & ESM.
const engines = '>=18.14.1';
const engines = '>=18.20.8';
const skipSemverCheckIfAbove = 19;
/** `astro *` */

88
node_modules/astro/client.d.ts generated vendored
View File

@@ -1,12 +1,8 @@
/// <reference types="vite/types/import-meta.d.ts" />
/// <reference path="./types/content.d.ts" />
/// <reference path="./types/actions.d.ts" />
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace App {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Locals {}
}
/// <reference path="./types/env.d.ts" />
/// <reference path="./types/fonts.d.ts" />
interface ImportMetaEnv {
/**
@@ -52,11 +48,12 @@ declare module 'astro:assets' {
getImage: (
options: import('./dist/assets/types.js').UnresolvedImageTransform,
) => Promise<import('./dist/assets/types.js').GetImageResult>;
imageConfig: import('./dist/@types/astro.js').AstroConfig['image'];
imageConfig: import('./dist/types/public/config.js').AstroConfig['image'];
getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
inferRemoteSize: typeof import('./dist/assets/utils/index.js').inferRemoteSize;
Image: typeof import('./components/Image.astro').default;
Picture: typeof import('./components/Picture.astro').default;
Font: typeof import('./components/Font.astro').default;
};
type ImgAttributes = import('./dist/type-utils.js').WithRequired<
@@ -76,6 +73,7 @@ declare module 'astro:assets' {
imageConfig,
Image,
Picture,
Font,
inferRemoteSize,
}: AstroAssets;
}
@@ -106,14 +104,16 @@ declare module '*.webp' {
const metadata: ImageMetadata;
export default metadata;
}
declare module '*.svg' {
const metadata: ImageMetadata;
export default metadata;
}
declare module '*.avif' {
const metadata: ImageMetadata;
export default metadata;
}
declare module '*.svg' {
type Props = astroHTML.JSX.SVGAttributes;
const Component: ((_props: Props) => any) & ImageMetadata;
export default Component;
}
declare module 'astro:transitions' {
type TransitionModule = typeof import('./dist/virtual-modules/transitions.js');
@@ -121,8 +121,12 @@ declare module 'astro:transitions' {
export const fade: TransitionModule['fade'];
export const createAnimationScope: TransitionModule['createAnimationScope'];
type ViewTransitionsModule = typeof import('./components/ViewTransitions.astro');
export const ViewTransitions: ViewTransitionsModule['default'];
type ClientRouterModule = typeof import('./components/ClientRouter.astro');
/**
* @deprecated The ViewTransitions component has been renamed to ClientRouter
*/
export const ViewTransitions: ClientRouterModule['default'];
export const ClientRouter: ClientRouterModule['default'];
}
declare module 'astro:transitions/client' {
@@ -134,8 +138,8 @@ declare module 'astro:transitions/client' {
export type Fallback = import('./dist/virtual-modules/transitions-types.js').Fallback;
export type Direction = import('./dist/virtual-modules/transitions-types.ts').Direction;
export type NavigationTypeString =
import('./dist/virtual-modules/transitions-types.js').NavigationTypeString;
// biome-ignore format: bug
export type NavigationTypeString = import('./dist/virtual-modules/transitions-types.js').NavigationTypeString;
export type Options = import('./dist/virtual-modules/transitions-types.js').Options;
type EventModule = typeof import('./dist/virtual-modules/transitions-events.js');
@@ -144,14 +148,14 @@ declare module 'astro:transitions/client' {
export const TRANSITION_BEFORE_SWAP: EventModule['TRANSITION_BEFORE_SWAP'];
export const TRANSITION_AFTER_SWAP: EventModule['TRANSITION_AFTER_SWAP'];
export const TRANSITION_PAGE_LOAD: EventModule['TRANSITION_PAGE_LOAD'];
export type TransitionBeforePreparationEvent =
import('./dist/virtual-modules/transitions-events.js').TransitionBeforePreparationEvent;
export type TransitionBeforeSwapEvent =
import('./dist/virtual-modules/transitions-events.js').TransitionBeforeSwapEvent;
// biome-ignore format: bug
export type TransitionBeforePreparationEvent = import('./dist/virtual-modules/transitions-events.js').TransitionBeforePreparationEvent;
// biome-ignore format: bug
export type TransitionBeforeSwapEvent = import('./dist/virtual-modules/transitions-events.js').TransitionBeforeSwapEvent;
export const isTransitionBeforePreparationEvent: EventModule['isTransitionBeforePreparationEvent'];
export const isTransitionBeforeSwapEvent: EventModule['isTransitionBeforeSwapEvent'];
type TransitionSwapFunctionModule =
typeof import('./dist/virtual-modules/transitions-swap-functions.js');
// biome-ignore format: bug
type TransitionSwapFunctionModule = typeof import('./dist/virtual-modules/transitions-swap-functions.js');
export const swapFunctions: TransitionSwapFunctionModule['swapFunctions'];
}
@@ -171,6 +175,20 @@ declare module 'astro:middleware' {
export * from 'astro/virtual-modules/middleware.js';
}
declare module 'astro:config/server' {
// biome-ignore format: bug
type ServerConfigSerialized = import('./dist/types/public/manifest.js').ServerDeserializedManifest;
const manifest: ServerConfigSerialized;
export = manifest;
}
declare module 'astro:config/client' {
// biome-ignore format: bug
type ClientConfigSerialized = import('./dist/types/public/manifest.js').ClientDeserializedManifest;
const manifest: ClientConfigSerialized;
export = manifest;
}
declare module 'astro:components' {
export * from 'astro/components';
}
@@ -179,7 +197,8 @@ declare module 'astro:schema' {
export * from 'astro/zod';
}
type MD = import('./dist/@types/astro.js').MarkdownInstance<Record<string, any>>;
type MD = import('./dist/types/public/content.js').MarkdownInstance<Record<string, any>>;
interface ExportedMarkdownModuleEntities {
frontmatter: MD['frontmatter'];
file: MD['file'];
@@ -198,7 +217,6 @@ declare module '*.md' {
file,
url,
getHeadings,
getHeaders,
Content,
rawContent,
compiledContent,
@@ -213,7 +231,6 @@ declare module '*.markdown' {
file,
url,
getHeadings,
getHeaders,
Content,
rawContent,
compiledContent,
@@ -228,7 +245,6 @@ declare module '*.mkdn' {
file,
url,
getHeadings,
getHeaders,
Content,
rawContent,
compiledContent,
@@ -243,7 +259,6 @@ declare module '*.mkd' {
file,
url,
getHeadings,
getHeaders,
Content,
rawContent,
compiledContent,
@@ -258,7 +273,6 @@ declare module '*.mdwn' {
file,
url,
getHeadings,
getHeaders,
Content,
rawContent,
compiledContent,
@@ -273,7 +287,6 @@ declare module '*.mdown' {
file,
url,
getHeadings,
getHeaders,
Content,
rawContent,
compiledContent,
@@ -282,7 +295,7 @@ declare module '*.mdown' {
}
declare module '*.mdx' {
type MDX = import('./dist/@types/astro.js').MDXInstance<Record<string, any>>;
type MDX = import('./dist/types/public/content.js').MDXInstance<Record<string, any>>;
export const frontmatter: MDX['frontmatter'];
export const file: MDX['file'];
@@ -296,7 +309,7 @@ declare module '*.mdx' {
}
declare module 'astro:ssr-manifest' {
export const manifest: import('./dist/@types/astro.js').SSRManifest;
export const manifest: import('./dist/types/public/internal.js').SSRManifest;
}
// Everything below are Vite's types (apart from image types, which are in `client.d.ts`)
@@ -522,3 +535,18 @@ declare module '*?inline' {
const src: string;
export default src;
}
declare module '*?no-inline' {
const src: string;
export default src;
}
declare module '*?url&inline' {
const src: string;
export default src;
}
declare module '*?url&no-inline' {
const src: string;
export default src;
}

View File

@@ -37,6 +37,7 @@ const { fallback = 'animate' } = Astro.props;
import { init } from 'astro/virtual-modules/prefetch.js';
type Fallback = 'none' | 'animate' | 'swap';
let lastClickedElementLeavingWindow: EventTarget | null = null;
function getFallback(): Fallback {
const el = document.querySelector('[name="astro-view-transitions-fallback"]');
@@ -50,6 +51,13 @@ const { fallback = 'animate' } = Astro.props;
return el.dataset.astroReload !== undefined;
}
const leavesWindow = (ev: MouseEvent) =>
(ev.button && ev.button !== 0) || // left clicks only
ev.metaKey || // new tab (mac)
ev.ctrlKey || // new tab (windows)
ev.altKey || // download
ev.shiftKey; // new window
if (supportsViewTransitions || getFallback() !== 'none') {
if (import.meta.env.DEV && window.matchMedia('(prefers-reduced-motion)').matches) {
console.warn(
@@ -58,6 +66,9 @@ const { fallback = 'animate' } = Astro.props;
}
document.addEventListener('click', (ev) => {
let link = ev.target;
lastClickedElementLeavingWindow = leavesWindow(ev) ? link : null;
if (ev.composed) {
link = ev.composedPath()[0];
}
@@ -82,11 +93,7 @@ const { fallback = 'animate' } = Astro.props;
!link.href ||
(linkTarget && linkTarget !== '_self') ||
origin !== location.origin ||
ev.button !== 0 || // left clicks only
ev.metaKey || // new tab (mac)
ev.ctrlKey || // new tab (windows)
ev.altKey || // download
ev.shiftKey || // new window
lastClickedElementLeavingWindow ||
ev.defaultPrevented
) {
// No page transitions in these cases,
@@ -102,11 +109,15 @@ const { fallback = 'animate' } = Astro.props;
document.addEventListener('submit', (ev) => {
let el = ev.target as HTMLElement;
if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el)) {
const submitter = ev.submitter;
const clickedWithKeys = submitter && submitter === lastClickedElementLeavingWindow;
lastClickedElementLeavingWindow = null;
if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el) || clickedWithKeys) {
return;
}
const form = el as HTMLFormElement;
const submitter = ev.submitter;
const formData = new FormData(form, submitter);
// form.action and form.method can point to an <input name="action"> or <input name="method">
// in which case should fallback to the form attribute

View File

@@ -1,15 +1,9 @@
---
import type { ThemePresets } from '@astrojs/markdown-remark';
import type {
BuiltinLanguage,
LanguageRegistration,
ShikiTransformer,
SpecialLanguage,
ThemeRegistration,
ThemeRegistrationRaw,
} from 'shiki';
import type { ShikiTransformer, ThemeRegistration, ThemeRegistrationRaw } from 'shiki';
import { bundledLanguages } from 'shiki/langs';
import { getCachedHighlighter } from '../dist/core/shiki.js';
import type { CodeLanguage } from '../dist/types/public';
import type { HTMLAttributes } from '../types';
interface Props extends Omit<HTMLAttributes<'pre'>, 'lang'> {
@@ -22,7 +16,7 @@ interface Props extends Omit<HTMLAttributes<'pre'>, 'lang'> {
*
* @default "plaintext"
*/
lang?: BuiltinLanguage | SpecialLanguage | LanguageRegistration;
lang?: CodeLanguage;
/**
* A metastring to pass to the highlighter.
* Allows passing information to transformers: https://shiki.style/guide/transformers#meta
@@ -111,13 +105,13 @@ const highlighter = await getCachedHighlighter({
],
theme,
themes,
defaultColor,
wrap,
transformers,
});
const html = await highlighter.highlight(code, typeof lang === 'string' ? lang : lang.name, {
const html = await highlighter.codeToHtml(code, typeof lang === 'string' ? lang : lang.name, {
defaultColor,
wrap,
inline,
transformers,
meta,
attributes: rest as any,
});

View File

@@ -21,8 +21,9 @@ const value = Astro.props[key];
font-size: 14px;
padding: 1rem 1.5rem;
background: white;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
'Open Sans', 'Helvetica Neue', sans-serif;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
'Helvetica Neue', sans-serif;
}
.astro-debug-header,

34
node_modules/astro/components/Font.astro generated vendored Normal file
View File

@@ -0,0 +1,34 @@
---
import * as mod from 'virtual:astro:assets/fonts/internal';
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
// TODO: remove check when fonts are stabilized
const { fontsData } = mod;
if (!fontsData) {
throw new AstroError(AstroErrorData.ExperimentalFontsNotEnabled);
}
interface Props {
/** The `cssVariable` registered in your Astro configuration. */
cssVariable: import('astro:assets').FontFamily;
/** Whether it should output [preload links](https://web.dev/learn/performance/optimize-web-fonts#preload) or not. */
preload?: boolean;
}
const { cssVariable, preload = false } = Astro.props as Props;
const data = fontsData.get(cssVariable);
if (!data) {
throw new AstroError({
...AstroErrorData.FontFamilyNotFound,
message: AstroErrorData.FontFamilyNotFound.message(cssVariable),
});
}
---
{
preload &&
data.preloadData.map(({ url, type }) => (
<link rel="preload" href={url} as="font" type={`font/${type}`} crossorigin />
))
}
<style set:html={data.css}></style>

View File

@@ -1,5 +1,6 @@
---
import { type LocalImageProps, type RemoteImageProps, getImage } from 'astro:assets';
import { getImage, imageConfig, type LocalImageProps, type RemoteImageProps } from 'astro:assets';
import type { UnresolvedImageTransform } from '../dist/assets/types';
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
import type { HTMLAttributes } from '../types';
@@ -23,7 +24,16 @@ if (typeof props.height === 'string') {
props.height = parseInt(props.height);
}
const image = await getImage(props);
const layout = props.layout ?? imageConfig.layout ?? 'none';
if (layout !== 'none') {
// Apply defaults from imageConfig if not provided
props.layout ??= imageConfig.layout;
props.fit ??= imageConfig.objectFit ?? 'cover';
props.position ??= imageConfig.objectPosition ?? 'center';
}
const image = await getImage(props as UnresolvedImageTransform);
const additionalAttributes: HTMLAttributes<'img'> = {};
if (image.srcSet.values.length > 0) {
@@ -33,6 +43,9 @@ if (image.srcSet.values.length > 0) {
if (import.meta.env.DEV) {
additionalAttributes['data-image-component'] = 'true';
}
const { class: className, ...attributes } = { ...additionalAttributes, ...image.attributes };
---
<img src={image.src} {...additionalAttributes} {...image.attributes} />
{/* Applying class outside of the spread prevents it from applying unnecessary astro-* classes */}
<img src={image.src} {...attributes} class={className} />

View File

@@ -1,12 +1,16 @@
---
import { type LocalImageProps, type RemoteImageProps, getImage } from 'astro:assets';
import { getImage, imageConfig, type LocalImageProps, type RemoteImageProps } from 'astro:assets';
import * as mime from 'mrmime';
import type { GetImageResult, ImageOutputFormat } from '../dist/@types/astro';
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind';
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind.js';
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
import type {
GetImageResult,
ImageOutputFormat,
UnresolvedImageTransform,
} from '../dist/types/public/index.js';
import type { HTMLAttributes } from '../types';
type Props = (LocalImageProps | RemoteImageProps) & {
export type Props = (LocalImageProps | RemoteImageProps) & {
formats?: ImageOutputFormat[];
fallbackFormat?: ImageOutputFormat;
pictureAttributes?: HTMLAttributes<'picture'>;
@@ -37,8 +41,20 @@ if (scopedStyleClass) {
pictureAttributes.class = scopedStyleClass;
}
}
const layout = props.layout ?? imageConfig.layout ?? 'none';
const useResponsive = layout !== 'none';
if (useResponsive) {
// Apply defaults from imageConfig if not provided
props.layout ??= imageConfig.layout;
props.fit ??= imageConfig.objectFit ?? 'cover';
props.position ??= imageConfig.objectPosition ?? 'center';
}
for (const key in props) {
if (key.startsWith('data-astro-cid')) {
// @ts-expect-error This is for island props so they're not properly typed
pictureAttributes[key] = props[key];
}
}
@@ -53,7 +69,7 @@ const optimizedImages: GetImageResult[] = await Promise.all(
format: format,
widths: props.widths,
densities: props.densities,
}),
} as UnresolvedImageTransform),
),
);
@@ -71,7 +87,7 @@ const fallbackImage = await getImage({
format: resultFallbackFormat,
widths: props.widths,
densities: props.densities,
});
} as UnresolvedImageTransform);
const imgAdditionalAttributes: HTMLAttributes<'img'> = {};
const sourceAdditionalAttributes: HTMLAttributes<'source'> = {};
@@ -88,13 +104,18 @@ if (fallbackImage.srcSet.values.length > 0) {
if (import.meta.env.DEV) {
imgAdditionalAttributes['data-image-component'] = 'true';
}
const { class: className, ...attributes } = {
...imgAdditionalAttributes,
...fallbackImage.attributes,
};
---
<picture {...pictureAttributes}>
{
Object.entries(optimizedImages).map(([_, image]) => {
const srcsetAttribute =
props.densities || (!props.densities && !props.widths)
props.densities || (!props.densities && !props.widths && !useResponsive)
? `${image.src}${image.srcSet.values.length > 0 ? ', ' + image.srcSet.attribute : ''}`
: image.srcSet.attribute;
return (
@@ -106,5 +127,6 @@ if (import.meta.env.DEV) {
);
})
}
<img src={fallbackImage.src} {...imgAdditionalAttributes} {...fallbackImage.attributes} />
{/* Applying class outside of the spread prevents it from applying unnecessary astro-* classes */}
<img src={fallbackImage.src} {...attributes} class={className} />
</picture>

13
node_modules/astro/components/ResponsiveImage.astro generated vendored Normal file
View File

@@ -0,0 +1,13 @@
---
import type { LocalImageProps, RemoteImageProps } from 'astro:assets';
import Image from './Image.astro';
type Props = LocalImageProps | RemoteImageProps;
const { class: className, ...props } = Astro.props;
import './image.css';
---
{/* Applying class outside of the spread prevents it from applying unnecessary astro-* classes */}
<Image {...props} class={className} />

12
node_modules/astro/components/ResponsivePicture.astro generated vendored Normal file
View File

@@ -0,0 +1,12 @@
---
import { default as Picture, type Props as PictureProps } from './Picture.astro';
type Props = PictureProps;
const { class: className, ...props } = Astro.props;
import './image.css';
---
{/* Applying class outside of the spread prevents it from applying unnecessary astro-* classes */}
<Picture {...props} class={className} />

2
node_modules/astro/components/env.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference path="../client.d.ts" />
/// <reference path="../dev-only.d.ts" />

11
node_modules/astro/components/image.css generated vendored Normal file
View File

@@ -0,0 +1,11 @@
:where([data-astro-image]) {
object-fit: var(--fit);
object-position: var(--pos);
height: auto;
}
:where([data-astro-image='full-width']) {
width: 100%;
}
:where([data-astro-image='constrained']) {
max-width: 100%;
}

View File

@@ -1,5 +1,5 @@
// The `ts-ignore` comments here are necessary because we're importing this file inside the `astro:components`
// virtual module's types, which means that `tsc` will try to resolve these imports. Don't mind the editor errors.
// virtual module's types, which means that `tsc` will try to resolve these imports.
// @ts-ignore
export { default as Code } from './Code.astro';
// @ts-ignore

48
node_modules/astro/config.d.ts generated vendored
View File

@@ -1,48 +0,0 @@
type ViteUserConfig = import('vite').UserConfig;
type ViteUserConfigFn = import('vite').UserConfigFn;
type AstroUserConfig = import('./dist/@types/astro.js').AstroUserConfig;
type AstroInlineConfig = import('./dist/@types/astro.js').AstroInlineConfig;
type ImageServiceConfig = import('./dist/@types/astro.js').ImageServiceConfig;
type SharpImageServiceConfig = import('./dist/assets/services/sharp.js').SharpImageServiceConfig;
type EnvField = typeof import('./dist/env/config.js').envField;
/**
* See the full Astro Configuration API Documentation
* https://astro.build/config
*/
export function defineConfig(config: AstroUserConfig): AstroUserConfig;
/**
* Use Astro to generate a fully resolved Vite config
*/
export function getViteConfig(
config: ViteUserConfig,
inlineAstroConfig?: AstroInlineConfig,
): ViteUserConfigFn;
/**
* Return the configuration needed to use the Sharp-based image service
*/
export function sharpImageService(config?: SharpImageServiceConfig): ImageServiceConfig;
/**
* @deprecated The Squoosh image service is deprecated and will be removed in Astro 5.x.
* We suggest migrating to the default Sharp image service instead, as it is faster, more powerful and better maintained.
*
* Return the configuration needed to use the Squoosh-based image service
* See: https://docs.astro.build/en/guides/images/#configure-squoosh
*/
export function squooshImageService(): ImageServiceConfig;
/**
* Return the configuration needed to use the passthrough image service. This image services does not perform
* any image transformations, and is mainly useful when your platform does not support other image services, or you are
* not using Astro's built-in image processing.
* See: https://docs.astro.build/en/guides/images/#configure-no-op-passthrough-service
*/
export function passthroughImageService(): ImageServiceConfig;
/**
* Return a valid env field to use in this Astro config for `experimental.env.schema`.
*/
export const envField: EnvField;

23
node_modules/astro/config.mjs generated vendored
View File

@@ -1,23 +0,0 @@
export { defineConfig, getViteConfig } from './dist/config/index.js';
export { envField } from './dist/env/config.js';
export function sharpImageService(config = {}) {
return {
entrypoint: 'astro/assets/services/sharp',
config,
};
}
export function squooshImageService() {
return {
entrypoint: 'astro/assets/services/squoosh',
config: {},
};
}
export function passthroughImageService() {
return {
entrypoint: 'astro/assets/services/noop',
config: {},
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
export declare const VIRTUAL_MODULE_ID = "astro:actions";
export declare const RESOLVED_VIRTUAL_MODULE_ID: string;
export declare const ACTIONS_TYPES_FILE = "astro/actions.d.ts";
export declare const VIRTUAL_INTERNAL_MODULE_ID = "astro:internal-actions";
export declare const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = "\0astro:internal-actions";
export declare const ACTIONS_TYPES_FILE = "actions.d.ts";
export declare const ASTRO_ACTIONS_INTERNAL_MODULE_ID = "astro-internal:actions";
export declare const RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID: string;
export declare const NOOP_ACTIONS = "\0noop-actions";
export declare const ACTION_QUERY_PARAMS: {
actionName: string;
actionPayload: string;
actionRedirect: string;
};
export declare const ACTION_RPC_ROUTE_PATTERN = "/_actions/[...path]";

View File

@@ -1,20 +1,21 @@
const VIRTUAL_MODULE_ID = "astro:actions";
const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
const ACTIONS_TYPES_FILE = "astro/actions.d.ts";
const VIRTUAL_INTERNAL_MODULE_ID = "astro:internal-actions";
const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = "\0astro:internal-actions";
const ACTIONS_TYPES_FILE = "actions.d.ts";
const ASTRO_ACTIONS_INTERNAL_MODULE_ID = "astro-internal:actions";
const RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID = "\0" + ASTRO_ACTIONS_INTERNAL_MODULE_ID;
const NOOP_ACTIONS = "\0noop-actions";
const ACTION_QUERY_PARAMS = {
actionName: "_astroAction",
actionPayload: "_astroActionPayload",
actionRedirect: "_astroActionRedirect"
actionName: "_action",
actionPayload: "_astroActionPayload"
};
const ACTION_RPC_ROUTE_PATTERN = "/_actions/[...path]";
export {
ACTIONS_TYPES_FILE,
ACTION_QUERY_PARAMS,
ACTION_RPC_ROUTE_PATTERN,
ASTRO_ACTIONS_INTERNAL_MODULE_ID,
NOOP_ACTIONS,
RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID,
RESOLVED_VIRTUAL_MODULE_ID,
VIRTUAL_INTERNAL_MODULE_ID,
VIRTUAL_MODULE_ID
};

View File

@@ -1,8 +1,10 @@
import type { AstroIntegration, AstroSettings } from '../@types/astro.js';
import type { AstroSettings } from '../types/astro.js';
import type { AstroIntegration } from '../types/public/integrations.js';
/**
* This integration is applied when the user is using Actions in their project.
* It will inject the necessary routes and middlewares to handle actions.
*/
export default function astroIntegrationActionsRouteHandler({ settings, }: {
export default function astroIntegrationActionsRouteHandler({ settings, filename, }: {
settings: AstroSettings;
filename: string;
}): AstroIntegration;

View File

@@ -1,32 +1,30 @@
import { ActionsWithoutServerOutputError } from "../core/errors/errors-data.js";
import { AstroError } from "../core/errors/errors.js";
import { isServerLikeOutput, viteID } from "../core/util.js";
import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from "./consts.js";
import { ActionsWithoutServerOutputError } from "../core/errors/errors-data.js";
import { viteID } from "../core/util.js";
import { ACTION_RPC_ROUTE_PATTERN, ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from "./consts.js";
function astroIntegrationActionsRouteHandler({
settings
settings,
filename
}) {
return {
name: VIRTUAL_MODULE_ID,
hooks: {
async "astro:config:setup"(params) {
params.injectRoute({
pattern: "/_actions/[...path]",
async "astro:config:setup"() {
settings.injectedRoutes.push({
pattern: ACTION_RPC_ROUTE_PATTERN,
entrypoint: "astro/actions/runtime/route.js",
prerender: false
});
params.addMiddleware({
entrypoint: "astro/actions/runtime/middleware.js",
order: "post"
prerender: false,
origin: "internal"
});
},
"astro:config:done": async (params) => {
if (!isServerLikeOutput(params.config)) {
if (params.buildOutput === "static") {
const error = new AstroError(ActionsWithoutServerOutputError);
error.stack = void 0;
throw error;
}
const stringifiedActionsImport = JSON.stringify(
viteID(new URL("./actions", params.config.srcDir))
viteID(new URL(`./${filename}`, params.config.srcDir))
);
settings.injectedTypes.push({
filename: ACTIONS_TYPES_FILE,

8
node_modules/astro/dist/actions/loadActions.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import type { SSRActions } from '../core/app/types.js';
import type { ModuleLoader } from '../core/module-loader/index.js';
/**
* It accepts a module loader and the astro settings, and it attempts to load the middlewares defined in the configuration.
*
* If not middlewares were not set, the function returns an empty array.
*/
export declare function loadActions(moduleLoader: ModuleLoader): Promise<SSRActions>;

13
node_modules/astro/dist/actions/loadActions.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import { ActionsCantBeLoaded } from "../core/errors/errors-data.js";
import { AstroError } from "../core/errors/index.js";
import { ASTRO_ACTIONS_INTERNAL_MODULE_ID } from "./consts.js";
async function loadActions(moduleLoader) {
try {
return await moduleLoader.import(ASTRO_ACTIONS_INTERNAL_MODULE_ID);
} catch (error) {
throw new AstroError(ActionsCantBeLoaded, { cause: error });
}
}
export {
loadActions
};

2
node_modules/astro/dist/actions/noop-actions.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import type { SSRActions } from '../core/app/types.js';
export declare const NOOP_ACTIONS_MOD: SSRActions;

6
node_modules/astro/dist/actions/noop-actions.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
const NOOP_ACTIONS_MOD = {
server: {}
};
export {
NOOP_ACTIONS_MOD
};

View File

@@ -1,6 +1,8 @@
import type fsMod from 'node:fs';
import type { Plugin as VitePlugin } from 'vite';
import type { AstroSettings } from '../@types/astro.js';
import type { BuildInternals } from '../core/build/internal.js';
import type { StaticBuildOptions } from '../core/build/types.js';
import type { AstroSettings } from '../types/astro.js';
/**
* This plugin is responsible to load the known file `actions/index.js` / `actions.js`
* If the file doesn't exist, it returns an empty object.
@@ -9,6 +11,12 @@ import type { AstroSettings } from '../@types/astro.js';
export declare function vitePluginUserActions({ settings }: {
settings: AstroSettings;
}): VitePlugin;
/**
* This plugin is used to retrieve the final entry point of the bundled actions.ts file
* @param opts
* @param internals
*/
export declare function vitePluginActionsBuild(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin;
export declare function vitePluginActions({ fs, settings, }: {
fs: typeof fsMod;
settings: AstroSettings;

View File

@@ -1,8 +1,11 @@
import { addRollupInput } from "../core/build/add-rollup-input.js";
import { shouldAppendForwardSlash } from "../core/build/util.js";
import { getServerOutputDirectory } from "../prerender/utils.js";
import {
ASTRO_ACTIONS_INTERNAL_MODULE_ID,
NOOP_ACTIONS,
RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID,
RESOLVED_VIRTUAL_MODULE_ID,
VIRTUAL_INTERNAL_MODULE_ID,
VIRTUAL_MODULE_ID
} from "./consts.js";
import { isActionsFilePresent } from "./utils.js";
@@ -14,7 +17,7 @@ function vitePluginUserActions({ settings }) {
if (id === NOOP_ACTIONS) {
return NOOP_ACTIONS;
}
if (id === VIRTUAL_INTERNAL_MODULE_ID) {
if (id === ASTRO_ACTIONS_INTERNAL_MODULE_ID) {
const resolvedModule = await this.resolve(
`${decodeURI(new URL("actions", settings.config.srcDir).pathname)}`
);
@@ -22,18 +25,34 @@ function vitePluginUserActions({ settings }) {
return NOOP_ACTIONS;
}
resolvedActionsId = resolvedModule.id;
return RESOLVED_VIRTUAL_INTERNAL_MODULE_ID;
return RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID;
}
},
load(id) {
if (id === NOOP_ACTIONS) {
return "export const server = {}";
} else if (id === RESOLVED_VIRTUAL_INTERNAL_MODULE_ID) {
} else if (id === RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID) {
return `export { server } from '${resolvedActionsId}';`;
}
}
};
}
function vitePluginActionsBuild(opts, internals) {
return {
name: "@astro/plugin-actions-build",
options(options) {
return addRollupInput(options, [ASTRO_ACTIONS_INTERNAL_MODULE_ID]);
},
writeBundle(_, bundle) {
for (const [chunkName, chunk] of Object.entries(bundle)) {
if (chunk.type !== "asset" && chunk.facadeModuleId === RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID) {
const outputDirectory = getServerOutputDirectory(opts.settings);
internals.astroActionsEntryPoint = new URL(chunkName, outputDirectory);
}
}
}
};
}
function vitePluginActions({
fs,
settings
@@ -70,11 +89,18 @@ export * from 'astro/actions/runtime/virtual/server.js';`;
code += `
export * from 'astro/actions/runtime/virtual/client.js';`;
}
return code;
code = code.replace(
"'/** @TRAILING_SLASH@ **/'",
JSON.stringify(
shouldAppendForwardSlash(settings.config.trailingSlash, settings.config.build.format)
)
);
return { code };
}
};
}
export {
vitePluginActions,
vitePluginActionsBuild,
vitePluginUserActions
};

View File

@@ -1,9 +0,0 @@
import { type SerializedActionResult } from './virtual/shared.js';
export type ActionPayload = {
actionResult: SerializedActionResult;
actionName: string;
};
export type Locals = {
_actionPayload: ActionPayload;
};
export declare const onRequest: import("../../@types/astro.js").MiddlewareHandler;

View File

@@ -1,110 +0,0 @@
import { yellow } from "kleur/colors";
import { defineMiddleware } from "../../core/middleware/index.js";
import { getOriginPathname } from "../../core/routing/rewrite.js";
import { ACTION_QUERY_PARAMS } from "../consts.js";
import { ACTION_API_CONTEXT_SYMBOL, formContentTypes, hasContentType } from "./utils.js";
import { getAction } from "./virtual/get-action.js";
import {
serializeActionResult
} from "./virtual/shared.js";
const onRequest = defineMiddleware(async (context, next) => {
if (context._isPrerendered) {
if (context.request.method === "POST") {
console.warn(
yellow("[astro:actions]"),
"POST requests should not be sent to prerendered pages. If you're using Actions, disable prerendering with `export const prerender = false`."
);
}
return next();
}
const locals = context.locals;
if (locals._actionPayload) return next();
const actionPayload = context.cookies.get(ACTION_QUERY_PARAMS.actionPayload)?.json();
if (actionPayload) {
if (!isActionPayload(actionPayload)) {
throw new Error("Internal: Invalid action payload in cookie.");
}
return renderResult({ context, next, ...actionPayload });
}
const actionName = context.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
if (context.request.method === "POST" && actionName) {
return handlePost({ context, next, actionName });
}
return next();
});
async function renderResult({
context,
next,
actionResult,
actionName
}) {
const locals = context.locals;
locals._actionPayload = { actionResult, actionName };
const response = await next();
context.cookies.delete(ACTION_QUERY_PARAMS.actionPayload);
if (actionResult.type === "error") {
return new Response(response.body, {
status: actionResult.status,
statusText: actionResult.type,
headers: response.headers
});
}
return response;
}
async function handlePost({
context,
next,
actionName
}) {
const { request } = context;
const baseAction = await getAction(actionName);
const contentType = request.headers.get("content-type");
let formData;
if (contentType && hasContentType(contentType, formContentTypes)) {
formData = await request.clone().formData();
}
const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context;
Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true);
const action = baseAction.bind(actionAPIContext);
const actionResult = await action(formData);
if (context.url.searchParams.get(ACTION_QUERY_PARAMS.actionRedirect) === "false") {
return renderResult({
context,
next,
actionName,
actionResult: serializeActionResult(actionResult)
});
}
return redirectWithResult({ context, actionName, actionResult });
}
async function redirectWithResult({
context,
actionName,
actionResult
}) {
context.cookies.set(ACTION_QUERY_PARAMS.actionPayload, {
actionName,
actionResult: serializeActionResult(actionResult)
});
if (actionResult.error) {
const referer2 = context.request.headers.get("Referer");
if (!referer2) {
throw new Error("Internal: Referer unexpectedly missing from Action POST request.");
}
return context.redirect(referer2);
}
const referer = getOriginPathname(context.request);
if (referer) {
return context.redirect(referer);
}
return context.redirect(context.url.pathname);
}
function isActionPayload(json) {
if (typeof json !== "object" || json == null) return false;
if (!("actionResult" in json) || typeof json.actionResult !== "object") return false;
if (!("actionName" in json) || typeof json.actionName !== "string") return false;
return true;
}
export {
onRequest
};

View File

@@ -1,2 +1,2 @@
import type { APIRoute } from '../../@types/astro.js';
import type { APIRoute } from '../../types/public/common.js';
export declare const POST: APIRoute;

View File

@@ -1,32 +1,10 @@
import { ACTION_API_CONTEXT_SYMBOL, formContentTypes, hasContentType } from "./utils.js";
import { getAction } from "./virtual/get-action.js";
import { serializeActionResult } from "./virtual/shared.js";
import { getActionContext } from "./virtual/server.js";
const POST = async (context) => {
const { request, url } = context;
let baseAction;
try {
baseAction = await getAction(url.pathname);
} catch (e) {
if (import.meta.env.DEV) throw e;
console.error(e);
return new Response(e instanceof Error ? e.message : null, { status: 404 });
const { action, serializeActionResult } = getActionContext(context);
if (action?.calledFrom !== "rpc") {
return new Response("Not found", { status: 404 });
}
const contentType = request.headers.get("Content-Type");
const contentLength = request.headers.get("Content-Length");
let args;
if (!contentType || contentLength === "0") {
args = void 0;
} else if (contentType && hasContentType(contentType, formContentTypes)) {
args = await request.clone().formData();
} else if (contentType && hasContentType(contentType, ["application/json"])) {
args = await request.clone().json();
} else {
return new Response(null, { status: 415 });
}
const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context;
Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true);
const action = baseAction.bind(actionAPIContext);
const result = await action(args);
const result = await action.handler();
const serialized = serializeActionResult(result);
if (serialized.type === "empty") {
return new Response(null, {

View File

@@ -1,8 +1,22 @@
import type { APIContext } from '../../@types/astro.js';
import type { APIContext, AstroSharedContext } from '../../types/public/context.js';
import type { SerializedActionResult } from './virtual/shared.js';
export type ActionPayload = {
actionResult: SerializedActionResult;
actionName: string;
};
export type Locals = {
_actionPayload: ActionPayload;
};
export declare const ACTION_API_CONTEXT_SYMBOL: unique symbol;
export declare const formContentTypes: string[];
export declare function hasContentType(contentType: string, expected: string[]): boolean;
export type ActionAPIContext = Omit<APIContext, 'getActionResult' | 'callAction' | 'props' | 'redirect'>;
export type ActionAPIContext = Pick<APIContext, 'rewrite' | 'request' | 'url' | 'isPrerendered' | 'locals' | 'clientAddress' | 'cookies' | 'currentLocale' | 'generator' | 'routePattern' | 'site' | 'params' | 'preferredLocale' | 'preferredLocaleList' | 'originPathname' | 'session' | 'insertDirective' | 'insertScriptResource' | 'insertStyleResource' | 'insertScriptHash' | 'insertStyleHash'> & {
/**
* @deprecated
* The use of `rewrite` in Actions is deprecated
*/
rewrite: AstroSharedContext['rewrite'];
};
export type MaybePromise<T> = T | Promise<T>;
/**
* Used to preserve the input schema type in the error object.

View File

@@ -1,2 +1,3 @@
export * from './shared.js';
export declare function defineAction(): void;
export declare function getActionContext(): void;

View File

@@ -2,6 +2,10 @@ export * from "./shared.js";
function defineAction() {
throw new Error("[astro:action] `defineAction()` unexpectedly used on the client.");
}
function getActionContext() {
throw new Error("[astro:action] `getActionContext()` unexpectedly used on the client.");
}
export {
defineAction
defineAction,
getActionContext
};

View File

@@ -1,8 +0,0 @@
import type { ZodType } from 'zod';
import type { ActionAccept, ActionClient } from './server.js';
/**
* Get server-side action based on the route path.
* Imports from the virtual module `astro:internal-actions`, which maps to
* the user's `src/actions/index.ts` file at build-time.
*/
export declare function getAction(path: string): Promise<ActionClient<unknown, ActionAccept, ZodType>>;

View File

@@ -1,29 +0,0 @@
import { ActionNotFoundError } from "../../../core/errors/errors-data.js";
import { AstroError } from "../../../core/errors/errors.js";
async function getAction(path) {
const pathKeys = path.replace(/^.*\/_actions\//, "").split(".").map((key) => decodeURIComponent(key));
let { server: actionLookup } = await import("astro:internal-actions");
if (actionLookup == null || !(typeof actionLookup === "object")) {
throw new TypeError(
`Expected \`server\` export in actions file to be an object. Received ${typeof actionLookup}.`
);
}
for (const key of pathKeys) {
if (!(key in actionLookup)) {
throw new AstroError({
...ActionNotFoundError,
message: ActionNotFoundError.message(pathKeys.join("."))
});
}
actionLookup = actionLookup[key];
}
if (typeof actionLookup !== "function") {
throw new TypeError(
`Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof actionLookup}.`
);
}
return actionLookup;
}
export {
getAction
};

View File

@@ -1,6 +1,7 @@
import { z } from 'zod';
import type { APIContext } from '../../../types/public/index.js';
import { type ActionAPIContext, type ErrorInferenceObject, type MaybePromise } from '../utils.js';
import { type SafeResult } from './shared.js';
import { deserializeActionResult, type SafeResult, type SerializedActionResult, serializeActionResult } from './shared.js';
export * from './shared.js';
export type ActionAccept = 'form' | 'json';
export type ActionHandler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
@@ -18,3 +19,32 @@ export declare function defineAction<TOutput, TAccept extends ActionAccept | und
}): ActionClient<TOutput, TAccept, TInputSchema> & string;
/** Transform form data to an object based on a Zod schema. */
export declare function formDataToObject<T extends z.AnyZodObject>(formData: FormData, schema: T): Record<string, unknown>;
export type AstroActionContext = {
/** Information about an incoming action request. */
action?: {
/** Whether an action was called using an RPC function or by using an HTML form action. */
calledFrom: 'rpc' | 'form';
/** The name of the action. Useful to track the source of an action result during a redirect. */
name: string;
/** Programmatically call the action to get the result. */
handler: () => Promise<SafeResult<any, any>>;
};
/**
* Manually set the action result accessed via `getActionResult()`.
* Calling this function from middleware will disable Astro's own action result handling.
*/
setActionResult(actionName: string, actionResult: SerializedActionResult): void;
/**
* Serialize an action result to stored in a cookie or session.
* Also used to pass a result to Astro templates via `setActionResult()`.
*/
serializeActionResult: typeof serializeActionResult;
/**
* Deserialize an action result to access data and error objects.
*/
deserializeActionResult: typeof deserializeActionResult;
};
/**
* Access information about Action requests from middleware.
*/
export declare function getActionContext(context: APIContext): AstroActionContext;

View File

@@ -1,10 +1,24 @@
import { z } from "zod";
import { ActionCalledFromServerError } from "../../../core/errors/errors-data.js";
import { shouldAppendForwardSlash } from "../../../core/build/util.js";
import { AstroError } from "../../../core/errors/errors.js";
import { ActionCalledFromServerError } from "../../../core/errors/errors-data.js";
import { removeTrailingForwardSlash } from "../../../core/path.js";
import { apiContextRoutesSymbol } from "../../../core/render-context.js";
import { ACTION_RPC_ROUTE_PATTERN } from "../../consts.js";
import {
ACTION_API_CONTEXT_SYMBOL,
formContentTypes,
hasContentType,
isActionAPIContext
} from "../utils.js";
import { ActionError, ActionInputError, callSafely } from "./shared.js";
import {
ACTION_QUERY_PARAMS,
ActionError,
ActionInputError,
callSafely,
deserializeActionResult,
serializeActionResult
} from "./shared.js";
export * from "./shared.js";
function defineAction({
accept,
@@ -122,7 +136,82 @@ function unwrapBaseObjectSchema(schema, unparsedInput) {
}
return schema;
}
function getActionContext(context) {
const callerInfo = getCallerInfo(context);
const actionResultAlreadySet = Boolean(context.locals._actionPayload);
let action = void 0;
if (callerInfo && context.request.method === "POST" && !actionResultAlreadySet) {
action = {
calledFrom: callerInfo.from,
name: callerInfo.name,
handler: async () => {
const pipeline = Reflect.get(context, apiContextRoutesSymbol);
const callerInfoName = shouldAppendForwardSlash(
pipeline.manifest.trailingSlash,
pipeline.manifest.buildFormat
) ? removeTrailingForwardSlash(callerInfo.name) : callerInfo.name;
const baseAction = await pipeline.getAction(callerInfoName);
let input;
try {
input = await parseRequestBody(context.request);
} catch (e) {
if (e instanceof TypeError) {
return { data: void 0, error: new ActionError({ code: "UNSUPPORTED_MEDIA_TYPE" }) };
}
throw e;
}
const omitKeys = ["props", "getActionResult", "callAction", "redirect"];
const actionAPIContext = Object.create(
Object.getPrototypeOf(context),
Object.fromEntries(
Object.entries(Object.getOwnPropertyDescriptors(context)).filter(
([key]) => !omitKeys.includes(key)
)
)
);
Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true);
const handler = baseAction.bind(actionAPIContext);
return handler(input);
}
};
}
function setActionResult(actionName, actionResult) {
context.locals._actionPayload = {
actionResult,
actionName
};
}
return {
action,
setActionResult,
serializeActionResult,
deserializeActionResult
};
}
function getCallerInfo(ctx) {
if (ctx.routePattern === ACTION_RPC_ROUTE_PATTERN) {
return { from: "rpc", name: ctx.url.pathname.replace(/^.*\/_actions\//, "") };
}
const queryParam = ctx.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
if (queryParam) {
return { from: "form", name: queryParam };
}
return void 0;
}
async function parseRequestBody(request) {
const contentType = request.headers.get("content-type");
const contentLength = request.headers.get("Content-Length");
if (!contentType) return void 0;
if (hasContentType(contentType, formContentTypes)) {
return await request.clone().formData();
}
if (hasContentType(contentType, ["application/json"])) {
return contentLength === "0" ? void 0 : await request.clone().json();
}
throw new TypeError("Unsupported content type");
}
export {
defineAction,
formDataToObject
formDataToObject,
getActionContext
};

View File

@@ -1,12 +1,14 @@
import type { z } from 'zod';
import type { ErrorInferenceObject, MaybePromise, ActionAPIContext as _ActionAPIContext } from '../utils.js';
import { AstroError } from '../../../core/errors/errors.js';
import { appendForwardSlash as _appendForwardSlash } from '../../../core/path.js';
import type { ActionAPIContext as _ActionAPIContext, ErrorInferenceObject, MaybePromise } from '../utils.js';
export type ActionAPIContext = _ActionAPIContext;
export declare const ACTION_QUERY_PARAMS: {
actionName: string;
actionPayload: string;
actionRedirect: string;
};
export declare const ACTION_ERROR_CODES: readonly ["BAD_REQUEST", "UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "TIMEOUT", "CONFLICT", "PRECONDITION_FAILED", "PAYLOAD_TOO_LARGE", "UNSUPPORTED_MEDIA_TYPE", "UNPROCESSABLE_CONTENT", "TOO_MANY_REQUESTS", "CLIENT_CLOSED_REQUEST", "INTERNAL_SERVER_ERROR"];
export declare const appendForwardSlash: typeof _appendForwardSlash;
export declare const ACTION_ERROR_CODES: readonly ["BAD_REQUEST", "UNAUTHORIZED", "PAYMENT_REQUIRED", "FORBIDDEN", "NOT_FOUND", "METHOD_NOT_ALLOWED", "NOT_ACCEPTABLE", "PROXY_AUTHENTICATION_REQUIRED", "REQUEST_TIMEOUT", "CONFLICT", "GONE", "LENGTH_REQUIRED", "PRECONDITION_FAILED", "CONTENT_TOO_LARGE", "URI_TOO_LONG", "UNSUPPORTED_MEDIA_TYPE", "RANGE_NOT_SATISFIABLE", "EXPECTATION_FAILED", "MISDIRECTED_REQUEST", "UNPROCESSABLE_CONTENT", "LOCKED", "FAILED_DEPENDENCY", "TOO_EARLY", "UPGRADE_REQUIRED", "PRECONDITION_REQUIRED", "TOO_MANY_REQUESTS", "REQUEST_HEADER_FIELDS_TOO_LARGE", "UNAVAILABLE_FOR_LEGAL_REASONS", "INTERNAL_SERVER_ERROR", "NOT_IMPLEMENTED", "BAD_GATEWAY", "SERVICE_UNAVAILABLE", "GATEWAY_TIMEOUT", "HTTP_VERSION_NOT_SUPPORTED", "VARIANT_ALSO_NEGOTIATES", "INSUFFICIENT_STORAGE", "LOOP_DETECTED", "NETWORK_AUTHENTICATION_REQUIRED"];
export type ActionErrorCode = (typeof ACTION_ERROR_CODES)[number];
export declare class ActionError<_T extends ErrorInferenceObject = ErrorInferenceObject> extends Error {
type: string;
@@ -55,3 +57,4 @@ export type SerializedActionResult = {
};
export declare function serializeActionResult(res: SafeResult<any, any>): SerializedActionResult;
export declare function deserializeActionResult(res: SerializedActionResult): SafeResult<any, any>;
export declare function astroCalledServerError(): AstroError;

View File

@@ -1,40 +1,95 @@
import { parse as devalueParse, stringify as devalueStringify } from "devalue";
import { REDIRECT_STATUS_CODES } from "../../../core/constants.js";
import { ActionsReturnedInvalidDataError } from "../../../core/errors/errors-data.js";
import { AstroError } from "../../../core/errors/errors.js";
import {
ActionCalledFromServerError,
ActionsReturnedInvalidDataError
} from "../../../core/errors/errors-data.js";
import { appendForwardSlash as _appendForwardSlash } from "../../../core/path.js";
import { ACTION_QUERY_PARAMS as _ACTION_QUERY_PARAMS } from "../../consts.js";
const ACTION_QUERY_PARAMS = _ACTION_QUERY_PARAMS;
const appendForwardSlash = _appendForwardSlash;
const ACTION_ERROR_CODES = [
"BAD_REQUEST",
"UNAUTHORIZED",
"PAYMENT_REQUIRED",
"FORBIDDEN",
"NOT_FOUND",
"TIMEOUT",
"METHOD_NOT_ALLOWED",
"NOT_ACCEPTABLE",
"PROXY_AUTHENTICATION_REQUIRED",
"REQUEST_TIMEOUT",
"CONFLICT",
"GONE",
"LENGTH_REQUIRED",
"PRECONDITION_FAILED",
"PAYLOAD_TOO_LARGE",
"CONTENT_TOO_LARGE",
"URI_TOO_LONG",
"UNSUPPORTED_MEDIA_TYPE",
"RANGE_NOT_SATISFIABLE",
"EXPECTATION_FAILED",
"MISDIRECTED_REQUEST",
"UNPROCESSABLE_CONTENT",
"LOCKED",
"FAILED_DEPENDENCY",
"TOO_EARLY",
"UPGRADE_REQUIRED",
"PRECONDITION_REQUIRED",
"TOO_MANY_REQUESTS",
"CLIENT_CLOSED_REQUEST",
"INTERNAL_SERVER_ERROR"
"REQUEST_HEADER_FIELDS_TOO_LARGE",
"UNAVAILABLE_FOR_LEGAL_REASONS",
"INTERNAL_SERVER_ERROR",
"NOT_IMPLEMENTED",
"BAD_GATEWAY",
"SERVICE_UNAVAILABLE",
"GATEWAY_TIMEOUT",
"HTTP_VERSION_NOT_SUPPORTED",
"VARIANT_ALSO_NEGOTIATES",
"INSUFFICIENT_STORAGE",
"LOOP_DETECTED",
"NETWORK_AUTHENTICATION_REQUIRED"
];
const codeToStatusMap = {
// Implemented from tRPC error code table
// https://trpc.io/docs/server/error-handling#error-codes
// Implemented from IANA HTTP Status Code Registry
// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
PAYMENT_REQUIRED: 402,
FORBIDDEN: 403,
NOT_FOUND: 404,
TIMEOUT: 405,
METHOD_NOT_ALLOWED: 405,
NOT_ACCEPTABLE: 406,
PROXY_AUTHENTICATION_REQUIRED: 407,
REQUEST_TIMEOUT: 408,
CONFLICT: 409,
GONE: 410,
LENGTH_REQUIRED: 411,
PRECONDITION_FAILED: 412,
PAYLOAD_TOO_LARGE: 413,
CONTENT_TOO_LARGE: 413,
URI_TOO_LONG: 414,
UNSUPPORTED_MEDIA_TYPE: 415,
RANGE_NOT_SATISFIABLE: 416,
EXPECTATION_FAILED: 417,
MISDIRECTED_REQUEST: 421,
UNPROCESSABLE_CONTENT: 422,
LOCKED: 423,
FAILED_DEPENDENCY: 424,
TOO_EARLY: 425,
UPGRADE_REQUIRED: 426,
PRECONDITION_REQUIRED: 428,
TOO_MANY_REQUESTS: 429,
CLIENT_CLOSED_REQUEST: 499,
INTERNAL_SERVER_ERROR: 500
REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
UNAVAILABLE_FOR_LEGAL_REASONS: 451,
INTERNAL_SERVER_ERROR: 500,
NOT_IMPLEMENTED: 501,
BAD_GATEWAY: 502,
SERVICE_UNAVAILABLE: 503,
GATEWAY_TIMEOUT: 504,
HTTP_VERSION_NOT_SUPPORTED: 505,
VARIANT_ALSO_NEGOTIATES: 506,
INSUFFICIENT_STORAGE: 507,
LOOP_DETECTED: 508,
NETWORK_AUTHENTICATION_REQUIRED: 511
};
const statusToCodeMap = Object.entries(codeToStatusMap).reduce(
// reverse the key-value pairs
@@ -126,14 +181,24 @@ function serializeActionResult(res) {
if (import.meta.env?.DEV) {
actionResultErrorStack.set(res.error.stack);
}
let body2;
if (res.error instanceof ActionInputError) {
body2 = {
type: res.error.type,
issues: res.error.issues,
fields: res.error.fields
};
} else {
body2 = {
...res.error,
message: res.error.message
};
}
return {
type: "error",
status: res.error.status,
contentType: "application/json",
body: JSON.stringify({
...res.error,
message: res.error.message
})
body: JSON.stringify(body2)
};
}
if (res.data === void 0) {
@@ -212,11 +277,16 @@ const actionResultErrorStack = /* @__PURE__ */ function actionResultErrorStackFn
}
};
}();
function astroCalledServerError() {
return new AstroError(ActionCalledFromServerError);
}
export {
ACTION_ERROR_CODES,
ACTION_QUERY_PARAMS,
ActionError,
ActionInputError,
appendForwardSlash,
astroCalledServerError,
callSafely,
deserializeActionResult,
getActionQueryString,

View File

@@ -1,11 +1,10 @@
import type fsMod from 'node:fs';
import type { APIContext } from '../@types/astro.js';
import type { Locals } from './runtime/middleware.js';
import { type ActionAPIContext } from './runtime/utils.js';
import type { APIContext } from '../types/public/context.js';
import { type ActionAPIContext, type Locals } from './runtime/utils.js';
export declare function hasActionPayload(locals: APIContext['locals']): locals is Locals;
export declare function createGetActionResult(locals: APIContext['locals']): APIContext['getActionResult'];
export declare function createCallAction(context: ActionAPIContext): APIContext['callAction'];
/**
* Check whether the Actions config file is present.
*/
export declare function isActionsFilePresent(fs: typeof fsMod, srcDir: URL): Promise<boolean>;
export declare function isActionsFilePresent(fs: typeof fsMod, srcDir: URL): Promise<string | false>;

View File

@@ -26,20 +26,20 @@ async function isActionsFilePresent(fs, srcDir) {
if (!actionsFile) return false;
let contents;
try {
contents = fs.readFileSync(actionsFile, "utf-8");
contents = fs.readFileSync(actionsFile.url, "utf-8");
} catch {
return false;
}
const [, exports] = eslexer.parse(contents, actionsFile.pathname);
const [, exports] = eslexer.parse(contents, actionsFile.url.pathname);
for (const exp of exports) {
if (exp.n === "server") {
return true;
return actionsFile.filename;
}
}
return false;
}
function search(fs, srcDir) {
const paths = [
const filenames = [
"actions.mjs",
"actions.js",
"actions.mts",
@@ -48,10 +48,11 @@ function search(fs, srcDir) {
"actions/index.js",
"actions/index.mts",
"actions/index.ts"
].map((p) => new URL(p, srcDir));
for (const file of paths) {
if (fs.existsSync(file)) {
return file;
];
for (const filename of filenames) {
const url = new URL(filename, srcDir);
if (fs.existsSync(url)) {
return { filename, url };
}
}
return void 0;

View File

@@ -1,8 +1,7 @@
import type PQueue from 'p-queue';
import type { AstroConfig } from '../../@types/astro.js';
import type { BuildPipeline } from '../../core/build/pipeline.js';
import type { Logger } from '../../core/logger/core.js';
import type { MapValue } from '../../type-utils.js';
import type { AstroConfig } from '../../types/public/config.js';
import type { AssetsGlobalStaticImagesList } from '../types.js';
type AssetEnv = {
logger: Logger;
@@ -19,6 +18,6 @@ type AssetEnv = {
assetsFolder: AstroConfig['build']['assets'];
};
export declare function prepareAssetsGenerationEnv(pipeline: BuildPipeline, totalCount: number): Promise<AssetEnv>;
export declare function generateImagesForPath(originalFilePath: string, transformsAndPath: MapValue<AssetsGlobalStaticImagesList>, env: AssetEnv, queue: PQueue): Promise<void>;
export declare function generateImagesForPath(originalFilePath: string, transformsAndPath: MapValue<AssetsGlobalStaticImagesList>, env: AssetEnv): Promise<void>;
export declare function getStaticImageList(): AssetsGlobalStaticImagesList;
export {};

View File

@@ -6,12 +6,11 @@ import { getTimeStat } from "../../core/build/util.js";
import { AstroError } from "../../core/errors/errors.js";
import { AstroErrorData } from "../../core/errors/index.js";
import { isRemotePath, removeLeadingForwardSlash } from "../../core/path.js";
import { isServerLikeOutput } from "../../core/util.js";
import { getConfiguredImageService } from "../internal.js";
import { isESMImportedImage } from "../utils/imageKind.js";
import { loadRemoteImage } from "./remote.js";
import { loadRemoteImage, revalidateRemoteImage } from "./remote.js";
async function prepareAssetsGenerationEnv(pipeline, totalCount) {
const { config, logger } = pipeline;
const { config, logger, settings } = pipeline;
let useCache = true;
const assetsCacheDir = new URL("assets/", config.cacheDir);
const count = { total: totalCount, current: 1 };
@@ -24,8 +23,9 @@ async function prepareAssetsGenerationEnv(pipeline, totalCount) {
);
useCache = false;
}
const isServerOutput = settings.buildOutput === "server";
let serverRoot, clientRoot;
if (isServerLikeOutput(config)) {
if (isServerOutput) {
serverRoot = config.build.server;
clientRoot = config.build.client;
} else {
@@ -34,7 +34,7 @@ async function prepareAssetsGenerationEnv(pipeline, totalCount) {
}
return {
logger,
isSSR: isServerLikeOutput(config),
isSSR: isServerOutput,
count,
useCache,
assetsCacheDir,
@@ -47,12 +47,10 @@ async function prepareAssetsGenerationEnv(pipeline, totalCount) {
function getFullImagePath(originalFilePath, env) {
return new URL(removeLeadingForwardSlash(originalFilePath), env.serverRoot);
}
async function generateImagesForPath(originalFilePath, transformsAndPath, env, queue) {
async function generateImagesForPath(originalFilePath, transformsAndPath, env) {
let originalImage;
for (const [_, transform] of transformsAndPath.transforms) {
await queue.add(async () => generateImage(transform.finalPath, transform.transform)).catch((e) => {
throw e;
});
await generateImage(transform.finalPath, transform.transform);
}
if (!env.isSSR && transformsAndPath.originalSrcPath && !globalThis.astroAsset.referencedImages?.has(transformsAndPath.originalSrcPath)) {
try {
@@ -72,7 +70,7 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
const timeEnd = performance.now();
const timeChange = getTimeStat(timeStart, timeEnd);
const timeIncrease = `(+${timeChange})`;
const statsText = generationData.cached ? `(reused cache entry)` : `(before: ${generationData.weight.before}kB, after: ${generationData.weight.after}kB)`;
const statsText = generationData.cached !== "miss" ? generationData.cached === "hit" ? `(reused cache entry)` : `(revalidated cache entry)` : `(before: ${generationData.weight.before}kB, after: ${generationData.weight.after}kB)`;
const count = `(${env.count.current}/${env.count.total})`;
env.logger.info(
null,
@@ -85,30 +83,69 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
const finalFileURL = new URL("." + filepath, env.clientRoot);
const finalFolderURL = new URL("./", finalFileURL);
await fs.promises.mkdir(finalFolderURL, { recursive: true });
const cacheFile = basename(filepath) + (isLocalImage ? "" : ".json");
const cacheFile = basename(filepath);
const cachedFileURL = new URL(cacheFile, env.assetsCacheDir);
const cacheMetaFile = cacheFile + ".json";
const cachedMetaFileURL = new URL(cacheMetaFile, env.assetsCacheDir);
try {
if (isLocalImage) {
await fs.promises.copyFile(cachedFileURL, finalFileURL, fs.constants.COPYFILE_FICLONE);
return {
cached: true
cached: "hit"
};
} else {
const JSONData = JSON.parse(readFileSync(cachedFileURL, "utf-8"));
if (!JSONData.data || !JSONData.expires) {
await fs.promises.unlink(cachedFileURL);
const JSONData = JSON.parse(readFileSync(cachedMetaFileURL, "utf-8"));
if (!JSONData.expires) {
try {
await fs.promises.unlink(cachedFileURL);
} catch {
}
await fs.promises.unlink(cachedMetaFileURL);
throw new Error(
`Malformed cache entry for ${filepath}, cache will be regenerated for this file.`
);
}
if (JSONData.expires > Date.now()) {
await fs.promises.writeFile(finalFileURL, Buffer.from(JSONData.data, "base64"));
return {
cached: true
};
} else {
await fs.promises.unlink(cachedFileURL);
if (JSONData.data) {
const { data, ...meta } = JSONData;
await Promise.all([
fs.promises.writeFile(cachedFileURL, Buffer.from(data, "base64")),
writeCacheMetaFile(cachedMetaFileURL, meta, env)
]);
}
if (JSONData.expires > Date.now()) {
await fs.promises.copyFile(cachedFileURL, finalFileURL, fs.constants.COPYFILE_FICLONE);
return {
cached: "hit"
};
}
if (JSONData.etag || JSONData.lastModified) {
try {
const revalidatedData = await revalidateRemoteImage(options.src, {
etag: JSONData.etag,
lastModified: JSONData.lastModified
});
if (revalidatedData.data.length) {
originalImage = revalidatedData;
} else {
await writeCacheMetaFile(cachedMetaFileURL, revalidatedData, env);
await fs.promises.copyFile(
cachedFileURL,
finalFileURL,
fs.constants.COPYFILE_FICLONE
);
return { cached: "revalidated" };
}
} catch (e) {
env.logger.warn(
null,
`An error was encountered while revalidating a cached remote asset. Proceeding with stale cache. ${e}`
);
await fs.promises.copyFile(cachedFileURL, finalFileURL, fs.constants.COPYFILE_FICLONE);
return { cached: "hit" };
}
}
await fs.promises.unlink(cachedFileURL);
await fs.promises.unlink(cachedMetaFileURL);
}
} catch (e) {
if (e.code !== "ENOENT") {
@@ -121,7 +158,9 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
}
let resultData = {
data: void 0,
expires: originalImage.expires
expires: originalImage.expires,
etag: originalImage.etag,
lastModified: originalImage.lastModified
};
const imageService = await getConfiguredImageService();
try {
@@ -131,6 +170,9 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
env.imageConfig
)).data;
} catch (e) {
if (AstroError.is(e)) {
throw e;
}
const error = new AstroError(
{
...AstroErrorData.CouldNotTransformImage,
@@ -145,13 +187,10 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
if (isLocalImage) {
await fs.promises.writeFile(cachedFileURL, resultData.data);
} else {
await fs.promises.writeFile(
cachedFileURL,
JSON.stringify({
data: Buffer.from(resultData.data).toString("base64"),
expires: resultData.expires
})
);
await Promise.all([
fs.promises.writeFile(cachedFileURL, resultData.data),
writeCacheMetaFile(cachedMetaFileURL, resultData, env)
]);
}
}
} catch (e) {
@@ -163,7 +202,7 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
await fs.promises.writeFile(finalFileURL, resultData.data);
}
return {
cached: false,
cached: "miss",
weight: {
// Divide by 1024 to get size in kilobytes
before: Math.trunc(originalImage.data.byteLength / 1024),
@@ -172,6 +211,23 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
};
}
}
async function writeCacheMetaFile(cachedMetaFileURL, resultData, env) {
try {
return await fs.promises.writeFile(
cachedMetaFileURL,
JSON.stringify({
expires: resultData.expires,
etag: resultData.etag,
lastModified: resultData.lastModified
})
);
} catch (e) {
env.logger.warn(
null,
`An error was encountered while writing the cache file for a remote asset. Proceeding without caching this asset. Error: ${e}`
);
}
}
function getStaticImageList() {
if (!globalThis?.astroAsset?.staticImages) {
return /* @__PURE__ */ new Map();
@@ -180,11 +236,7 @@ function getStaticImageList() {
}
async function loadImage(path, env) {
if (isRemotePath(path)) {
const remoteImage = await loadRemoteImage(path);
return {
data: remoteImage.data,
expires: remoteImage.expires
};
return await loadRemoteImage(path);
}
return {
data: await fs.promises.readFile(getFullImagePath(path, env)),

View File

@@ -1,8 +1,30 @@
export type RemoteCacheEntry = {
data: string;
data?: string;
expires: number;
etag?: string;
lastModified?: string;
};
export declare function loadRemoteImage(src: string): Promise<{
data: Buffer;
expires: number;
etag: string | undefined;
lastModified: string | undefined;
}>;
/**
* Revalidate a cached remote asset using its entity-tag or modified date.
* Uses the [If-None-Match](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) and [If-Modified-Since](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since)
* headers to check with the remote server if the cached version of a remote asset is still up to date.
* The remote server may respond that the cached asset is still up-to-date if the entity-tag or modification time matches (304 Not Modified), or respond with an updated asset (200 OK)
* @param src - url to remote asset
* @param revalidationData - an object containing the stored Entity-Tag of the cached asset and/or the Last Modified time
* @returns An ImageData object containing the asset data, a new expiry time, and the asset's etag. The data buffer will be empty if the asset was not modified.
*/
export declare function revalidateRemoteImage(src: string, revalidationData: {
etag?: string;
lastModified?: string;
}): Promise<{
data: Buffer;
expires: number;
etag: string | undefined;
lastModified: string | undefined;
}>;

View File

@@ -11,7 +11,41 @@ async function loadRemoteImage(src) {
const expires = policy.storable() ? policy.timeToLive() : 0;
return {
data: Buffer.from(await res.arrayBuffer()),
expires: Date.now() + expires
expires: Date.now() + expires,
etag: res.headers.get("Etag") ?? void 0,
lastModified: res.headers.get("Last-Modified") ?? void 0
};
}
async function revalidateRemoteImage(src, revalidationData) {
const headers = {
...revalidationData.etag && { "If-None-Match": revalidationData.etag },
...revalidationData.lastModified && { "If-Modified-Since": revalidationData.lastModified }
};
const req = new Request(src, { headers });
const res = await fetch(req);
if (!res.ok && res.status !== 304) {
throw new Error(
`Failed to revalidate cached remote image ${src}. The request did not return a 200 OK / 304 NOT MODIFIED response. (received ${res.status} ${res.statusText})`
);
}
const data = Buffer.from(await res.arrayBuffer());
if (res.ok && !data.length) {
return await loadRemoteImage(src);
}
const policy = new CachePolicy(
webToCachePolicyRequest(req),
webToCachePolicyResponse(
res.ok ? res : new Response(null, { status: 200, headers: res.headers })
)
// 304 responses themselves are not cacheable, so just pretend to get the refreshed TTL
);
const expires = policy.storable() ? policy.timeToLive() : 0;
return {
data,
expires: Date.now() + expires,
// While servers should respond with the same headers as a 200 response, if they don't we should reuse the stored value
etag: res.headers.get("Etag") ?? (res.ok ? void 0 : revalidationData.etag),
lastModified: res.headers.get("Last-Modified") ?? (res.ok ? void 0 : revalidationData.lastModified)
};
}
function webToCachePolicyRequest({ url, method, headers: _headers }) {
@@ -38,5 +72,6 @@ function webToCachePolicyResponse({ status, headers: _headers }) {
};
}
export {
loadRemoteImage
loadRemoteImage,
revalidateRemoteImage
};

View File

@@ -22,7 +22,15 @@ const VALID_SUPPORTED_FORMATS = [
];
const DEFAULT_OUTPUT_FORMAT = "webp";
const VALID_OUTPUT_FORMATS = ["avif", "png", "webp", "jpeg", "jpg", "svg"];
const DEFAULT_HASH_PROPS = ["src", "width", "height", "format", "quality"];
const DEFAULT_HASH_PROPS = [
"src",
"width",
"height",
"format",
"quality",
"fit",
"position"
];
export {
DEFAULT_HASH_PROPS,
DEFAULT_OUTPUT_FORMAT,

View File

@@ -1,2 +1,2 @@
import type { AstroSettings } from '../../@types/astro.js';
export declare function injectImageEndpoint(settings: AstroSettings, mode: 'dev' | 'build'): AstroSettings;
import type { AstroSettings, RoutesList } from '../../types/astro.js';
export declare function injectImageEndpoint(settings: AstroSettings, manifest: RoutesList, mode: 'dev' | 'build', cwd?: string): void;

View File

@@ -1,11 +1,39 @@
function injectImageEndpoint(settings, mode) {
const endpointEntrypoint = settings.config.image.endpoint ?? (mode === "dev" ? "astro/assets/endpoint/node" : "astro/assets/endpoint/generic");
settings.injectedRoutes.push({
pattern: "/_image",
entrypoint: endpointEntrypoint,
prerender: false
});
return settings;
import {
removeLeadingForwardSlash,
removeTrailingForwardSlash
} from "@astrojs/internal-helpers/path";
import { resolveInjectedRoute } from "../../core/routing/manifest/create.js";
import { getPattern } from "../../core/routing/manifest/pattern.js";
function injectImageEndpoint(settings, manifest, mode, cwd) {
manifest.routes.unshift(getImageEndpointData(settings, mode, cwd));
}
function getImageEndpointData(settings, mode, cwd) {
const endpointEntrypoint = settings.config.image.endpoint.entrypoint === void 0 ? mode === "dev" ? "astro/assets/endpoint/node" : "astro/assets/endpoint/generic" : settings.config.image.endpoint.entrypoint;
const segments = [
[
{
content: removeTrailingForwardSlash(
removeLeadingForwardSlash(settings.config.image.endpoint.route)
),
dynamic: false,
spread: false
}
]
];
return {
type: "endpoint",
isIndex: false,
route: settings.config.image.endpoint.route,
pattern: getPattern(segments, settings.config.base, settings.config.trailingSlash),
segments,
params: [],
component: resolveInjectedRoute(endpointEntrypoint, settings.config.root, cwd).component,
generate: () => "",
pathname: settings.config.image.endpoint.route,
prerender: false,
fallbackRoutes: [],
origin: "internal"
};
}
export {
injectImageEndpoint

View File

@@ -1,4 +1,4 @@
import type { APIRoute } from '../../@types/astro.js';
import type { APIRoute } from '../../types/public/common.js';
/**
* Endpoint used in dev and SSR to serve optimized images by the base image services
*/

View File

@@ -1,9 +1,9 @@
import { imageConfig } from "astro:assets";
import { isRemotePath } from "@astrojs/internal-helpers/path";
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
import * as mime from "mrmime";
import { getConfiguredImageService } from "../internal.js";
import { etag } from "../utils/etag.js";
import { isRemoteAllowed } from "../utils/remotePattern.js";
async function loadRemoteImage(src, headers) {
try {
const res = await fetch(src, {

View File

@@ -1,4 +1,4 @@
import type { APIRoute } from '../../@types/astro.js';
import type { APIRoute } from '../../types/public/common.js';
/**
* Endpoint used in dev and SSR to serve optimized images by the base image services
*/

View File

@@ -1,13 +1,13 @@
import { assetsDir, imageConfig, outDir } from "astro:assets";
import { readFile } from "node:fs/promises";
import os from "node:os";
import { isAbsolute } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { assetsDir, imageConfig, outDir } from "astro:assets";
import { isRemotePath, removeQueryString } from "@astrojs/internal-helpers/path";
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
import * as mime from "mrmime";
import { getConfiguredImageService } from "../internal.js";
import { etag } from "../utils/etag.js";
import { isRemoteAllowed } from "../utils/remotePattern.js";
function replaceFileSystemReferences(src) {
return os.platform().includes("win32") ? src.replace(/^\/@fs\//, "") : src.replace(/^\/@fs/, "");
}

363
node_modules/astro/dist/assets/fonts/config.d.ts generated vendored Normal file
View File

@@ -0,0 +1,363 @@
import { z } from 'zod';
export declare const styleSchema: z.ZodEnum<["normal", "italic", "oblique"]>;
export declare const fontProviderSchema: z.ZodObject<{
/**
* URL, path relative to the root or package import.
*/
entrypoint: z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>;
/**
* Optional serializable object passed to the unifont provider.
*/
config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
}, "strict", z.ZodTypeAny, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}>;
export declare const localFontFamilySchema: z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<{
/**
* The font family name, as identified by your font provider.
*/
name: z.ZodString;
/**
* A valid [ident](https://developer.mozilla.org/en-US/docs/Web/CSS/ident) in the form of a CSS variable (i.e. starting with `--`).
*/
cssVariable: z.ZodString;
}, {
/**
* @default `["sans-serif"]`
*
* An array of fonts to use when your chosen font is unavailable, or loading. Fallback fonts will be chosen in the order listed. The first available font will be used:
*
* ```js
* fallbacks: ["CustomFont", "serif"]
* ```
*
* To disable fallback fonts completely, configure an empty array:
*
* ```js
* fallbacks: []
* ```
*
* If the last font in the `fallbacks` array is a [generic family name](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#generic-name), Astro will attempt to generate [optimized fallbacks](https://developer.chrome.com/blog/font-fallbacks) using font metrics will be generated. To disable this optimization, set `optimizedFallbacks` to false.
*/
fallbacks: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
/**
* @default `true`
*
* Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`.
*/
optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
}>, {
/**
* The source of your font files. Set to `"local"` to use local font files.
*/
provider: z.ZodLiteral<"local">;
/**
* Each variant represents a [`@font-face` declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/).
*/
variants: z.ZodArray<z.ZodObject<z.objectUtil.extendShape<{
/**
* A [font weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:
*
* ```js
* weight: "100 900"
* ```
*/
weight: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
/**
* A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
style: z.ZodOptional<z.ZodEnum<["normal", "italic", "oblique"]>>;
/**
* @default `"swap"`
*
* A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display).
*/
display: z.ZodOptional<z.ZodEnum<["auto", "block", "swap", "fallback", "optional"]>>;
/**
* A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch).
*/
stretch: z.ZodOptional<z.ZodString>;
/**
* Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings).
*/
featureSettings: z.ZodOptional<z.ZodString>;
/**
* Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings).
*/
variationSettings: z.ZodOptional<z.ZodString>;
}, {
/**
* Font [sources](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src). It can be a path relative to the root, a package import or a URL. URLs are particularly useful if you inject local fonts through an integration.
*/
src: z.ZodArray<z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>, z.ZodObject<{
url: z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>;
tech: z.ZodOptional<z.ZodString>;
}, "strict", z.ZodTypeAny, {
url: string | URL;
tech?: string | undefined;
}, {
url: string | URL;
tech?: string | undefined;
}>]>, "atleastone">;
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
}>, "strict", z.ZodTypeAny, {
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, {
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}>, "atleastone">;
}>, "strict", z.ZodTypeAny, {
name: string;
cssVariable: string;
provider: "local";
variants: [{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, ...{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}[]];
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
}, {
name: string;
cssVariable: string;
provider: "local";
variants: [{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, ...{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}[]];
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
}>;
export declare const remoteFontFamilySchema: z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<z.objectUtil.extendShape<{
/**
* The font family name, as identified by your font provider.
*/
name: z.ZodString;
/**
* A valid [ident](https://developer.mozilla.org/en-US/docs/Web/CSS/ident) in the form of a CSS variable (i.e. starting with `--`).
*/
cssVariable: z.ZodString;
}, Omit<{
/**
* A [font weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:
*
* ```js
* weight: "100 900"
* ```
*/
weight: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
/**
* A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
style: z.ZodOptional<z.ZodEnum<["normal", "italic", "oblique"]>>;
/**
* @default `"swap"`
*
* A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display).
*/
display: z.ZodOptional<z.ZodEnum<["auto", "block", "swap", "fallback", "optional"]>>;
/**
* A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch).
*/
stretch: z.ZodOptional<z.ZodString>;
/**
* Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings).
*/
featureSettings: z.ZodOptional<z.ZodString>;
/**
* Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings).
*/
variationSettings: z.ZodOptional<z.ZodString>;
}, "weight" | "style">>, {
/**
* @default `["sans-serif"]`
*
* An array of fonts to use when your chosen font is unavailable, or loading. Fallback fonts will be chosen in the order listed. The first available font will be used:
*
* ```js
* fallbacks: ["CustomFont", "serif"]
* ```
*
* To disable fallback fonts completely, configure an empty array:
*
* ```js
* fallbacks: []
* ```
*
* If the last font in the `fallbacks` array is a [generic family name](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#generic-name), Astro will attempt to generate [optimized fallbacks](https://developer.chrome.com/blog/font-fallbacks) using font metrics will be generated. To disable this optimization, set `optimizedFallbacks` to false.
*/
fallbacks: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
/**
* @default `true`
*
* Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`.
*/
optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
}>, {
/**
* The source of your font files. You can use a built-in provider or write your own custom provider.
*/
provider: z.ZodObject<{
/**
* URL, path relative to the root or package import.
*/
entrypoint: z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>;
/**
* Optional serializable object passed to the unifont provider.
*/
config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
}, "strict", z.ZodTypeAny, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}>;
/**
* @default `[400]`
*
* An array of [font weights](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:
*
* ```js
* weight: "100 900"
* ```
*/
weights: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "atleastone">>;
/**
* @default `["normal", "italic"]`
*
* An array of [font styles](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
styles: z.ZodOptional<z.ZodArray<z.ZodEnum<["normal", "italic", "oblique"]>, "atleastone">>;
/**
* @default `["cyrillic-ext", "cyrillic", "greek-ext", "greek", "vietnamese", "latin-ext", "latin"]`
*
* An array of [font subsets](https://knaap.dev/posts/font-subsetting/):
*/
subsets: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
}>, "strict", z.ZodTypeAny, {
name: string;
cssVariable: string;
provider: {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
weights?: [string | number, ...(string | number)[]] | undefined;
styles?: ["normal" | "italic" | "oblique", ...("normal" | "italic" | "oblique")[]] | undefined;
subsets?: [string, ...string[]] | undefined;
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, {
name: string;
cssVariable: string;
provider: {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
weights?: [string | number, ...(string | number)[]] | undefined;
styles?: ["normal" | "italic" | "oblique", ...("normal" | "italic" | "oblique")[]] | undefined;
subsets?: [string, ...string[]] | undefined;
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}>;

161
node_modules/astro/dist/assets/fonts/config.js generated vendored Normal file
View File

@@ -0,0 +1,161 @@
import { z } from "zod";
import { LOCAL_PROVIDER_NAME } from "./constants.js";
const weightSchema = z.union([z.string(), z.number()]);
const styleSchema = z.enum(["normal", "italic", "oblique"]);
const unicodeRangeSchema = z.array(z.string()).nonempty();
const familyPropertiesSchema = z.object({
/**
* A [font weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:
*
* ```js
* weight: "100 900"
* ```
*/
weight: weightSchema.optional(),
/**
* A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
style: styleSchema.optional(),
/**
* @default `"swap"`
*
* A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display).
*/
display: z.enum(["auto", "block", "swap", "fallback", "optional"]).optional(),
/**
* A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch).
*/
stretch: z.string().optional(),
/**
* Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings).
*/
featureSettings: z.string().optional(),
/**
* Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings).
*/
variationSettings: z.string().optional()
});
const fallbacksSchema = z.object({
/**
* @default `["sans-serif"]`
*
* An array of fonts to use when your chosen font is unavailable, or loading. Fallback fonts will be chosen in the order listed. The first available font will be used:
*
* ```js
* fallbacks: ["CustomFont", "serif"]
* ```
*
* To disable fallback fonts completely, configure an empty array:
*
* ```js
* fallbacks: []
* ```
*
* If the last font in the `fallbacks` array is a [generic family name](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#generic-name), Astro will attempt to generate [optimized fallbacks](https://developer.chrome.com/blog/font-fallbacks) using font metrics will be generated. To disable this optimization, set `optimizedFallbacks` to false.
*/
fallbacks: z.array(z.string()).optional(),
/**
* @default `true`
*
* Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`.
*/
optimizedFallbacks: z.boolean().optional()
});
const requiredFamilyAttributesSchema = z.object({
/**
* The font family name, as identified by your font provider.
*/
name: z.string(),
/**
* A valid [ident](https://developer.mozilla.org/en-US/docs/Web/CSS/ident) in the form of a CSS variable (i.e. starting with `--`).
*/
cssVariable: z.string()
});
const entrypointSchema = z.union([z.string(), z.instanceof(URL)]);
const fontProviderSchema = z.object({
/**
* URL, path relative to the root or package import.
*/
entrypoint: entrypointSchema,
/**
* Optional serializable object passed to the unifont provider.
*/
config: z.record(z.string(), z.any()).optional()
}).strict();
const localFontFamilySchema = requiredFamilyAttributesSchema.merge(fallbacksSchema).merge(
z.object({
/**
* The source of your font files. Set to `"local"` to use local font files.
*/
provider: z.literal(LOCAL_PROVIDER_NAME),
/**
* Each variant represents a [`@font-face` declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/).
*/
variants: z.array(
familyPropertiesSchema.merge(
z.object({
/**
* Font [sources](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src). It can be a path relative to the root, a package import or a URL. URLs are particularly useful if you inject local fonts through an integration.
*/
src: z.array(
z.union([
entrypointSchema,
z.object({ url: entrypointSchema, tech: z.string().optional() }).strict()
])
).nonempty(),
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: unicodeRangeSchema.optional()
// TODO: find a way to support subsets (through fontkit?)
}).strict()
)
).nonempty()
})
).strict();
const remoteFontFamilySchema = requiredFamilyAttributesSchema.merge(
familyPropertiesSchema.omit({
weight: true,
style: true
})
).merge(fallbacksSchema).merge(
z.object({
/**
* The source of your font files. You can use a built-in provider or write your own custom provider.
*/
provider: fontProviderSchema,
/**
* @default `[400]`
*
* An array of [font weights](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:
*
* ```js
* weight: "100 900"
* ```
*/
weights: z.array(weightSchema).nonempty().optional(),
/**
* @default `["normal", "italic"]`
*
* An array of [font styles](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
styles: z.array(styleSchema).nonempty().optional(),
/**
* @default `["cyrillic-ext", "cyrillic", "greek-ext", "greek", "vietnamese", "latin-ext", "latin"]`
*
* An array of [font subsets](https://knaap.dev/posts/font-subsetting/):
*/
subsets: z.array(z.string()).nonempty().optional(),
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: unicodeRangeSchema.optional()
})
).strict();
export {
fontProviderSchema,
localFontFamilySchema,
remoteFontFamilySchema,
styleSchema
};

14
node_modules/astro/dist/assets/fonts/constants.d.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import type { Defaults, FontType } from './types.js';
export declare const LOCAL_PROVIDER_NAME = "local";
export declare const DEFAULTS: Defaults;
export declare const VIRTUAL_MODULE_ID = "virtual:astro:assets/fonts/internal";
export declare const RESOLVED_VIRTUAL_MODULE_ID: string;
export declare const ASSETS_DIR = "fonts";
export declare const CACHE_DIR = "./fonts/";
export declare const FONT_TYPES: readonly ["woff2", "woff", "otf", "ttf", "eot"];
export declare const FONT_FORMATS: Array<{
type: FontType;
format: string;
}>;
export declare const GENERIC_FALLBACK_NAMES: readonly ["serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded", "emoji", "math", "fangsong"];
export declare const FONTS_TYPES_FILE = "fonts.d.ts";

49
node_modules/astro/dist/assets/fonts/constants.js generated vendored Normal file
View File

@@ -0,0 +1,49 @@
const LOCAL_PROVIDER_NAME = "local";
const DEFAULTS = {
weights: ["400"],
styles: ["normal", "italic"],
subsets: ["cyrillic-ext", "cyrillic", "greek-ext", "greek", "vietnamese", "latin-ext", "latin"],
// Technically serif is the browser default but most websites these days use sans-serif
fallbacks: ["sans-serif"],
optimizedFallbacks: true
};
const VIRTUAL_MODULE_ID = "virtual:astro:assets/fonts/internal";
const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
const ASSETS_DIR = "fonts";
const CACHE_DIR = "./fonts/";
const FONT_TYPES = ["woff2", "woff", "otf", "ttf", "eot"];
const FONT_FORMATS = [
{ type: "woff2", format: "woff2" },
{ type: "woff", format: "woff" },
{ type: "otf", format: "opentype" },
{ type: "ttf", format: "truetype" },
{ type: "eot", format: "embedded-opentype" }
];
const GENERIC_FALLBACK_NAMES = [
"serif",
"sans-serif",
"monospace",
"cursive",
"fantasy",
"system-ui",
"ui-serif",
"ui-sans-serif",
"ui-monospace",
"ui-rounded",
"emoji",
"math",
"fangsong"
];
const FONTS_TYPES_FILE = "fonts.d.ts";
export {
ASSETS_DIR,
CACHE_DIR,
DEFAULTS,
FONTS_TYPES_FILE,
FONT_FORMATS,
FONT_TYPES,
GENERIC_FALLBACK_NAMES,
LOCAL_PROVIDER_NAME,
RESOLVED_VIRTUAL_MODULE_ID,
VIRTUAL_MODULE_ID
};

88
node_modules/astro/dist/assets/fonts/definitions.d.ts generated vendored Normal file
View File

@@ -0,0 +1,88 @@
import type * as unifont from 'unifont';
import type { CollectedFontForMetrics } from './logic/optimize-fallbacks.js';
import type { AstroFontProvider, FontFaceMetrics, FontFileData, FontType, GenericFallbackName, PreloadData, ResolvedFontProvider, Style } from './types.js';
export interface Hasher {
hashString: (input: string) => string;
hashObject: (input: Record<string, any>) => string;
}
export interface RemoteFontProviderModResolver {
resolve: (id: string) => Promise<any>;
}
export interface RemoteFontProviderResolver {
resolve: (provider: AstroFontProvider) => Promise<ResolvedFontProvider>;
}
export interface LocalProviderUrlResolver {
resolve: (input: string) => string;
}
type SingleErrorInput<TType extends string, TData extends Record<string, any>> = {
type: TType;
data: TData;
cause: unknown;
};
export type ErrorHandlerInput = SingleErrorInput<'cannot-load-font-provider', {
entrypoint: string;
}> | SingleErrorInput<'unknown-fs-error', {}> | SingleErrorInput<'cannot-fetch-font-file', {
url: string;
}> | SingleErrorInput<'cannot-extract-font-type', {
url: string;
}> | SingleErrorInput<'cannot-extract-data', {
family: string;
url: string;
}>;
export interface ErrorHandler {
handle: (input: ErrorHandlerInput) => Error;
}
export interface UrlProxy {
proxy: (input: Pick<FontFileData, 'url' | 'init'> & {
type: FontType;
collectPreload: boolean;
data: Partial<unifont.FontFaceData>;
}) => string;
}
export interface UrlResolver {
resolve: (hash: string) => string;
}
export interface UrlProxyContentResolver {
resolve: (url: string) => string;
}
export interface DataCollector {
collect: (input: FontFileData & {
data: Partial<unifont.FontFaceData>;
preload: PreloadData | null;
}) => void;
}
export type CssProperties = Record<string, string | undefined>;
export interface CssRenderer {
generateFontFace: (family: string, properties: CssProperties) => string;
generateCssVariable: (key: string, values: Array<string>) => string;
}
export interface FontMetricsResolver {
getMetrics: (name: string, font: CollectedFontForMetrics) => Promise<FontFaceMetrics>;
generateFontFace: (input: {
metrics: FontFaceMetrics;
fallbackMetrics: FontFaceMetrics;
name: string;
font: string;
properties: CssProperties;
}) => string;
}
export interface SystemFallbacksProvider {
getLocalFonts: (fallback: GenericFallbackName) => Array<string> | null;
getMetricsForLocalFont: (family: string) => FontFaceMetrics;
}
export interface FontFetcher {
fetch: (input: FontFileData) => Promise<Buffer>;
}
export interface FontTypeExtractor {
extract: (url: string) => FontType;
}
export interface FontFileReader {
extract: (input: {
family: string;
url: string;
}) => {
weight: string;
style: Style;
};
}
export {};

View File

@@ -0,0 +1,9 @@
import type { CssProperties, CssRenderer } from '../definitions.js';
export declare function renderFontFace(properties: CssProperties, minify: boolean): string;
export declare function renderCssVariable(key: string, values: Array<string>, minify: boolean): string;
export declare function withFamily(family: string, properties: CssProperties): CssProperties;
/** If the value contains spaces (which would be incorrectly interpreted), we wrap it in quotes. */
export declare function handleValueWithSpaces(value: string): string;
export declare function createMinifiableCssRenderer({ minify }: {
minify: boolean;
}): CssRenderer;

View File

@@ -0,0 +1,42 @@
function renderFontFace(properties, minify) {
const lf = minify ? "" : `
`;
const sp = minify ? "" : " ";
return `@font-face${sp}{${lf}${Object.entries(properties).filter(([, value]) => Boolean(value)).map(([key, value]) => `${sp}${sp}${key}:${sp}${value};`).join(lf)}${lf}}${lf}`;
}
function renderCssVariable(key, values, minify) {
const lf = minify ? "" : `
`;
const sp = minify ? "" : " ";
return `:root${sp}{${lf}${sp}${sp}${key}:${sp}${values.map((v) => handleValueWithSpaces(v)).join(`,${sp}`)};${lf}}${lf}`;
}
function withFamily(family, properties) {
return {
"font-family": handleValueWithSpaces(family),
...properties
};
}
const SPACE_RE = /\s/;
function handleValueWithSpaces(value) {
if (SPACE_RE.test(value)) {
return JSON.stringify(value);
}
return value;
}
function createMinifiableCssRenderer({ minify }) {
return {
generateFontFace(family, properties) {
return renderFontFace(withFamily(family, properties), minify);
},
generateCssVariable(key, values) {
return renderCssVariable(key, values, minify);
}
};
}
export {
createMinifiableCssRenderer,
handleValueWithSpaces,
renderCssVariable,
renderFontFace,
withFamily
};

View File

@@ -0,0 +1,3 @@
import type { DataCollector } from '../definitions.js';
import type { CreateUrlProxyParams } from '../types.js';
export declare function createDataCollector({ hasUrl, saveUrl, savePreload, saveFontData, }: Omit<CreateUrlProxyParams, 'local'>): DataCollector;

View File

@@ -0,0 +1,21 @@
function createDataCollector({
hasUrl,
saveUrl,
savePreload,
saveFontData
}) {
return {
collect({ hash, url, init, preload, data }) {
if (!hasUrl(hash)) {
saveUrl({ hash, url, init });
if (preload) {
savePreload(preload);
}
}
saveFontData({ hash, url, data, init });
}
};
}
export {
createDataCollector
};

View File

@@ -0,0 +1,2 @@
import type { ErrorHandler } from '../definitions.js';
export declare function createAstroErrorHandler(): ErrorHandler;

View File

@@ -0,0 +1,41 @@
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
function getProps(input) {
if (input.type === "cannot-load-font-provider") {
return {
...AstroErrorData.CannotLoadFontProvider,
message: AstroErrorData.CannotLoadFontProvider.message(input.data.entrypoint)
};
} else if (input.type === "unknown-fs-error") {
return AstroErrorData.UnknownFilesystemError;
} else if (input.type === "cannot-fetch-font-file") {
return {
...AstroErrorData.CannotFetchFontFile,
message: AstroErrorData.CannotFetchFontFile.message(input.data.url)
};
} else if (input.type === "cannot-extract-font-type") {
return {
...AstroErrorData.CannotExtractFontType,
message: AstroErrorData.CannotExtractFontType.message(input.data.url)
};
} else if (input.type === "cannot-extract-data") {
return {
...AstroErrorData.CannotDetermineWeightAndStyleFromFontFile,
message: AstroErrorData.CannotDetermineWeightAndStyleFromFontFile.message(
input.data.family,
input.data.url
)
};
}
input;
return AstroErrorData.UnknownError;
}
function createAstroErrorHandler() {
return {
handle(input) {
return new AstroError(getProps(input), { cause: input.cause });
}
};
}
export {
createAstroErrorHandler
};

View File

@@ -0,0 +1,8 @@
import type { Storage } from 'unstorage';
import type { ErrorHandler, FontFetcher } from '../definitions.js';
export declare function createCachedFontFetcher({ storage, errorHandler, fetch, readFile, }: {
storage: Storage;
errorHandler: ErrorHandler;
fetch: (url: string, init?: RequestInit) => Promise<Response>;
readFile: (url: string) => Promise<Buffer>;
}): FontFetcher;

View File

@@ -0,0 +1,34 @@
import { isAbsolute } from "node:path";
import { cache } from "../utils.js";
function createCachedFontFetcher({
storage,
errorHandler,
fetch,
readFile
}) {
return {
async fetch({ hash, url, init }) {
return await cache(storage, hash, async () => {
try {
if (isAbsolute(url)) {
return await readFile(url);
}
const response = await fetch(url, init ?? void 0);
if (!response.ok) {
throw new Error(`Response was not successful, received status code ${response.status}`);
}
return Buffer.from(await response.arrayBuffer());
} catch (cause) {
throw errorHandler.handle({
type: "cannot-fetch-font-file",
data: { url },
cause
});
}
});
}
};
}
export {
createCachedFontFetcher
};

View File

@@ -0,0 +1,4 @@
import type { ErrorHandler, FontFileReader } from '../definitions.js';
export declare function createFontaceFontFileReader({ errorHandler, }: {
errorHandler: ErrorHandler;
}): FontFileReader;

View File

@@ -0,0 +1,26 @@
import { readFileSync } from "node:fs";
import { fontace } from "fontace";
function createFontaceFontFileReader({
errorHandler
}) {
return {
extract({ family, url }) {
try {
const data = fontace(readFileSync(url));
return {
weight: data.weight,
style: data.style
};
} catch (cause) {
throw errorHandler.handle({
type: "cannot-extract-data",
data: { family, url },
cause
});
}
}
};
}
export {
createFontaceFontFileReader
};

View File

@@ -0,0 +1,5 @@
import type { CssRenderer, FontFetcher, FontMetricsResolver } from '../definitions.js';
export declare function createCapsizeFontMetricsResolver({ fontFetcher, cssRenderer, }: {
fontFetcher: FontFetcher;
cssRenderer: CssRenderer;
}): FontMetricsResolver;

View File

@@ -0,0 +1,60 @@
import { fromBuffer } from "@capsizecss/unpack";
import { renderFontSrc } from "../utils.js";
function filterRequiredMetrics({
ascent,
descent,
lineGap,
unitsPerEm,
xWidthAvg
}) {
return {
ascent,
descent,
lineGap,
unitsPerEm,
xWidthAvg
};
}
function toPercentage(value, fractionDigits = 4) {
const percentage = value * 100;
return `${+percentage.toFixed(fractionDigits)}%`;
}
function createCapsizeFontMetricsResolver({
fontFetcher,
cssRenderer
}) {
const cache = {};
return {
async getMetrics(name, input) {
cache[name] ??= filterRequiredMetrics(await fromBuffer(await fontFetcher.fetch(input)));
return cache[name];
},
// Source: https://github.com/unjs/fontaine/blob/f00f84032c5d5da72c8798eae4cd68d3ddfbf340/src/css.ts#L170
generateFontFace({
metrics,
fallbackMetrics,
name: fallbackName,
font: fallbackFontName,
properties
}) {
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
const sizeAdjust = preferredFontXAvgRatio / fallbackFontXAvgRatio;
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust;
const ascentOverride = metrics.ascent / adjustedEmSquare;
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare;
const lineGapOverride = metrics.lineGap / adjustedEmSquare;
return cssRenderer.generateFontFace(fallbackName, {
...properties,
src: renderFontSrc([{ name: fallbackFontName }]),
"size-adjust": toPercentage(sizeAdjust),
"ascent-override": toPercentage(ascentOverride),
"descent-override": toPercentage(descentOverride),
"line-gap-override": toPercentage(lineGapOverride)
});
}
};
}
export {
createCapsizeFontMetricsResolver
};

View File

@@ -0,0 +1,4 @@
import type { ErrorHandler, FontTypeExtractor } from '../definitions.js';
export declare function createFontTypeExtractor({ errorHandler, }: {
errorHandler: ErrorHandler;
}): FontTypeExtractor;

View File

@@ -0,0 +1,22 @@
import { extname } from "node:path";
import { isFontType } from "../utils.js";
function createFontTypeExtractor({
errorHandler
}) {
return {
extract(url) {
const extension = extname(url).slice(1);
if (!isFontType(extension)) {
throw errorHandler.handle({
type: "cannot-extract-font-type",
data: { url },
cause: `Unexpected extension, got "${extension}"`
});
}
return extension;
}
};
}
export {
createFontTypeExtractor
};

View File

@@ -0,0 +1,2 @@
import type { Hasher } from '../definitions.js';
export declare function createXxHasher(): Promise<Hasher>;

View File

@@ -0,0 +1,14 @@
import xxhash from "xxhash-wasm";
import { sortObjectByKey } from "../utils.js";
async function createXxHasher() {
const { h64ToString: hashString } = await xxhash();
return {
hashString,
hashObject(input) {
return hashString(JSON.stringify(sortObjectByKey(input)));
}
};
}
export {
createXxHasher
};

View File

@@ -0,0 +1,5 @@
import type { LocalProviderUrlResolver } from '../definitions.js';
export declare function createRequireLocalProviderUrlResolver({ root, intercept, }: {
root: URL;
intercept?: (path: string) => void;
}): LocalProviderUrlResolver;

View File

@@ -0,0 +1,17 @@
import { fileURLToPath } from "node:url";
import { resolveEntrypoint } from "../utils.js";
function createRequireLocalProviderUrlResolver({
root,
intercept
}) {
return {
resolve(input) {
const path = fileURLToPath(resolveEntrypoint(root, input));
intercept?.(path);
return path;
}
};
}
export {
createRequireLocalProviderUrlResolver
};

View File

@@ -0,0 +1,6 @@
import type { ViteDevServer } from 'vite';
import type { RemoteFontProviderModResolver } from '../definitions.js';
export declare function createBuildRemoteFontProviderModResolver(): RemoteFontProviderModResolver;
export declare function createDevServerRemoteFontProviderModResolver({ server, }: {
server: ViteDevServer;
}): RemoteFontProviderModResolver;

View File

@@ -0,0 +1,20 @@
function createBuildRemoteFontProviderModResolver() {
return {
resolve(id) {
return import(id);
}
};
}
function createDevServerRemoteFontProviderModResolver({
server
}) {
return {
resolve(id) {
return server.ssrLoadModule(id);
}
};
}
export {
createBuildRemoteFontProviderModResolver,
createDevServerRemoteFontProviderModResolver
};

View File

@@ -0,0 +1,6 @@
import type { ErrorHandler, RemoteFontProviderModResolver, RemoteFontProviderResolver } from '../definitions.js';
export declare function createRemoteFontProviderResolver({ root, modResolver, errorHandler, }: {
root: URL;
modResolver: RemoteFontProviderModResolver;
errorHandler: ErrorHandler;
}): RemoteFontProviderResolver;

View File

@@ -0,0 +1,47 @@
import { resolveEntrypoint } from "../utils.js";
function validateMod({
mod,
entrypoint,
errorHandler
}) {
try {
if (typeof mod !== "object" || mod === null) {
throw new Error(`Expected an object for the module, but received ${typeof mod}.`);
}
if (typeof mod.provider !== "function") {
throw new Error(`Invalid provider export in module, expected a function.`);
}
return {
provider: mod.provider
};
} catch (cause) {
throw errorHandler.handle({
type: "cannot-load-font-provider",
data: {
entrypoint
},
cause
});
}
}
function createRemoteFontProviderResolver({
root,
modResolver,
errorHandler
}) {
return {
async resolve({ entrypoint, config }) {
const id = resolveEntrypoint(root, entrypoint.toString()).href;
const mod = await modResolver.resolve(id);
const { provider } = validateMod({
mod,
entrypoint: id,
errorHandler
});
return { config, provider };
}
};
}
export {
createRemoteFontProviderResolver
};

View File

@@ -0,0 +1,4 @@
import { type Storage } from 'unstorage';
export declare function createFsStorage({ base }: {
base: URL;
}): Storage;

View File

@@ -0,0 +1,14 @@
import { fileURLToPath } from "node:url";
import { createStorage } from "unstorage";
import fsLiteDriver from "unstorage/drivers/fs-lite";
function createFsStorage({ base }) {
return createStorage({
// Types are weirly exported
driver: fsLiteDriver({
base: fileURLToPath(base)
})
});
}
export {
createFsStorage
};

View File

@@ -0,0 +1,11 @@
import type { SystemFallbacksProvider } from '../definitions.js';
export declare const DEFAULT_FALLBACKS: {
serif: "Times New Roman"[];
'sans-serif': "Arial"[];
monospace: "Courier New"[];
'system-ui': ("Arial" | "BlinkMacSystemFont" | "Segoe UI" | "Roboto" | "Helvetica Neue")[];
'ui-serif': "Times New Roman"[];
'ui-sans-serif': "Arial"[];
'ui-monospace': "Courier New"[];
};
export declare function createSystemFallbacksProvider(): SystemFallbacksProvider;

View File

@@ -0,0 +1,74 @@
const SYSTEM_METRICS = {
"Times New Roman": {
ascent: 1825,
descent: -443,
lineGap: 87,
unitsPerEm: 2048,
xWidthAvg: 832
},
Arial: {
ascent: 1854,
descent: -434,
lineGap: 67,
unitsPerEm: 2048,
xWidthAvg: 913
},
"Courier New": {
ascent: 1705,
descent: -615,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 1229
},
BlinkMacSystemFont: {
ascent: 1980,
descent: -432,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 853
},
"Segoe UI": {
ascent: 2210,
descent: -514,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 908
},
Roboto: {
ascent: 1900,
descent: -500,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 911
},
"Helvetica Neue": {
ascent: 952,
descent: -213,
lineGap: 28,
unitsPerEm: 1e3,
xWidthAvg: 450
}
};
const DEFAULT_FALLBACKS = {
serif: ["Times New Roman"],
"sans-serif": ["Arial"],
monospace: ["Courier New"],
"system-ui": ["BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial"],
"ui-serif": ["Times New Roman"],
"ui-sans-serif": ["Arial"],
"ui-monospace": ["Courier New"]
};
function createSystemFallbacksProvider() {
return {
getLocalFonts(fallback) {
return DEFAULT_FALLBACKS[fallback] ?? null;
},
getMetricsForLocalFont(family) {
return SYSTEM_METRICS[family];
}
};
}
export {
DEFAULT_FALLBACKS,
createSystemFallbacksProvider
};

View File

@@ -0,0 +1,5 @@
import type { ErrorHandler, UrlProxyContentResolver } from '../definitions.js';
export declare function createLocalUrlProxyContentResolver({ errorHandler, }: {
errorHandler: ErrorHandler;
}): UrlProxyContentResolver;
export declare function createRemoteUrlProxyContentResolver(): UrlProxyContentResolver;

View File

@@ -0,0 +1,28 @@
import { readFileSync } from "node:fs";
function createLocalUrlProxyContentResolver({
errorHandler
}) {
return {
resolve(url) {
try {
return url + readFileSync(url, "utf-8");
} catch (cause) {
throw errorHandler.handle({
type: "unknown-fs-error",
data: {},
cause
});
}
}
};
}
function createRemoteUrlProxyContentResolver() {
return {
// Passthrough, the remote provider URL is enough
resolve: (url) => url
};
}
export {
createLocalUrlProxyContentResolver,
createRemoteUrlProxyContentResolver
};

View File

@@ -0,0 +1,7 @@
import type { DataCollector, Hasher, UrlProxy, UrlProxyContentResolver, UrlResolver } from '../definitions.js';
export declare function createUrlProxy({ contentResolver, hasher, dataCollector, urlResolver, }: {
contentResolver: UrlProxyContentResolver;
hasher: Hasher;
dataCollector: DataCollector;
urlResolver: UrlResolver;
}): UrlProxy;

View File

@@ -0,0 +1,24 @@
function createUrlProxy({
contentResolver,
hasher,
dataCollector,
urlResolver
}) {
return {
proxy({ url: originalUrl, type, data, collectPreload, init }) {
const hash = `${hasher.hashString(contentResolver.resolve(originalUrl))}.${type}`;
const url = urlResolver.resolve(hash);
dataCollector.collect({
url: originalUrl,
hash,
preload: collectPreload ? { url, type } : null,
data,
init
});
return url;
}
};
}
export {
createUrlProxy
};

View File

@@ -0,0 +1,9 @@
import type { AssetsPrefix } from '../../../types/public/index.js';
import type { UrlResolver } from '../definitions.js';
export declare function createDevUrlResolver({ base }: {
base: string;
}): UrlResolver;
export declare function createBuildUrlResolver({ base, assetsPrefix, }: {
base: string;
assetsPrefix: AssetsPrefix;
}): UrlResolver;

View File

@@ -0,0 +1,27 @@
import { fileExtension, joinPaths, prependForwardSlash } from "../../../core/path.js";
import { getAssetsPrefix } from "../../utils/getAssetsPrefix.js";
function createDevUrlResolver({ base }) {
return {
resolve(hash) {
return prependForwardSlash(joinPaths(base, hash));
}
};
}
function createBuildUrlResolver({
base,
assetsPrefix
}) {
return {
resolve(hash) {
const prefix = assetsPrefix ? getAssetsPrefix(fileExtension(hash), assetsPrefix) : void 0;
if (prefix) {
return joinPaths(prefix, base, hash);
}
return prependForwardSlash(joinPaths(base, hash));
}
};
}
export {
createBuildUrlResolver,
createDevUrlResolver
};

View File

@@ -0,0 +1,10 @@
import type * as unifont from 'unifont';
import type { Hasher } from '../definitions.js';
import type { ResolvedFontFamily } from '../types.js';
export declare function extractUnifontProviders({ families, hasher, }: {
families: Array<ResolvedFontFamily>;
hasher: Hasher;
}): {
families: Array<ResolvedFontFamily>;
providers: Array<unifont.Provider>;
};

View File

@@ -0,0 +1,28 @@
import { LOCAL_PROVIDER_NAME } from "../constants.js";
function extractUnifontProviders({
families,
hasher
}) {
const hashes = /* @__PURE__ */ new Set();
const providers = [];
for (const { provider } of families) {
if (provider === LOCAL_PROVIDER_NAME) {
continue;
}
const unifontProvider = provider.provider(provider.config);
const hash = hasher.hashObject({
name: unifontProvider._name,
...provider.config
});
unifontProvider._name += `-${hash}`;
provider.name = unifontProvider._name;
if (!hashes.has(hash)) {
hashes.add(hash);
providers.push(unifontProvider);
}
}
return { families, providers };
}
export {
extractUnifontProviders
};

View File

@@ -0,0 +1,7 @@
import type * as unifont from 'unifont';
import type { FontTypeExtractor, UrlProxy } from '../definitions.js';
export declare function normalizeRemoteFontFaces({ fonts, urlProxy, fontTypeExtractor, }: {
fonts: Array<unifont.FontFaceData>;
urlProxy: UrlProxy;
fontTypeExtractor: FontTypeExtractor;
}): Array<unifont.FontFaceData>;

View File

@@ -0,0 +1,40 @@
import { FONT_FORMATS } from "../constants.js";
function normalizeRemoteFontFaces({
fonts,
urlProxy,
fontTypeExtractor
}) {
return fonts.filter((font) => typeof font.meta?.priority === "number" ? font.meta.priority === 0 : true).map((font) => {
let index = 0;
return {
...font,
src: font.src.map((source) => {
if ("name" in source) {
return source;
}
const url = source.url.startsWith("//") ? `https:${source.url}` : source.url;
const proxied = {
...source,
originalURL: url,
url: urlProxy.proxy({
url,
type: FONT_FORMATS.find((e) => e.format === source.format)?.type ?? fontTypeExtractor.extract(source.url),
// We only collect the first URL to avoid preloading fallback sources (eg. we only
// preload woff2 if woff is available)
collectPreload: index === 0,
data: {
weight: font.weight,
style: font.style
},
init: font.meta?.init ?? null
})
};
index++;
return proxied;
})
};
});
}
export {
normalizeRemoteFontFaces
};

View File

@@ -0,0 +1,17 @@
import type * as unifont from 'unifont';
import type { FontMetricsResolver, SystemFallbacksProvider } from '../definitions.js';
import type { FontFileData, ResolvedFontFamily } from '../types.js';
export interface CollectedFontForMetrics extends FontFileData {
data: Partial<unifont.FontFaceData>;
}
export declare function optimizeFallbacks({ family, fallbacks: _fallbacks, collectedFonts, enabled, systemFallbacksProvider, fontMetricsResolver, }: {
family: Pick<ResolvedFontFamily, 'name' | 'nameWithHash'>;
fallbacks: Array<string>;
collectedFonts: Array<CollectedFontForMetrics>;
enabled: boolean;
systemFallbacksProvider: SystemFallbacksProvider;
fontMetricsResolver: FontMetricsResolver;
}): Promise<null | {
css: string;
fallbacks: Array<string>;
}>;

View File

@@ -0,0 +1,47 @@
import { isGenericFontFamily, unifontFontFaceDataToProperties } from "../utils.js";
async function optimizeFallbacks({
family,
fallbacks: _fallbacks,
collectedFonts,
enabled,
systemFallbacksProvider,
fontMetricsResolver
}) {
let fallbacks = [..._fallbacks];
if (fallbacks.length === 0 || !enabled || collectedFonts.length === 0) {
return null;
}
const lastFallback = fallbacks[fallbacks.length - 1];
if (!isGenericFontFamily(lastFallback)) {
return null;
}
const localFonts = systemFallbacksProvider.getLocalFonts(lastFallback);
if (!localFonts || localFonts.length === 0) {
return null;
}
if (localFonts.includes(family.name)) {
return null;
}
const localFontsMappings = localFonts.map((font) => ({
font,
// We must't wrap in quote because that's handled by the CSS renderer
name: `${family.nameWithHash} fallback: ${font}`
}));
fallbacks = [...localFontsMappings.map((m) => m.name), ...fallbacks];
let css = "";
for (const { font, name } of localFontsMappings) {
for (const collected of collectedFonts) {
css += fontMetricsResolver.generateFontFace({
metrics: await fontMetricsResolver.getMetrics(family.name, collected),
fallbackMetrics: systemFallbacksProvider.getMetricsForLocalFont(font),
font,
name,
properties: unifontFontFaceDataToProperties(collected.data)
});
}
}
return { css, fallbacks };
}
export {
optimizeFallbacks
};

View File

@@ -0,0 +1,17 @@
import type { Hasher, LocalProviderUrlResolver, RemoteFontProviderResolver } from '../definitions.js';
import type { FontFamily, ResolvedFontFamily } from '../types.js';
/**
* Dedupes properties if applicable and resolves entrypoints.
*/
export declare function resolveFamily({ family, hasher, remoteFontProviderResolver, localProviderUrlResolver, }: {
family: FontFamily;
hasher: Hasher;
remoteFontProviderResolver: RemoteFontProviderResolver;
localProviderUrlResolver: LocalProviderUrlResolver;
}): Promise<ResolvedFontFamily>;
/**
* A function for convenience. The actual logic lives in resolveFamily
*/
export declare function resolveFamilies({ families, ...dependencies }: {
families: Array<FontFamily>;
} & Omit<Parameters<typeof resolveFamily>[0], 'family'>): Promise<Array<ResolvedFontFamily>>;

View File

@@ -0,0 +1,67 @@
import { LOCAL_PROVIDER_NAME } from "../constants.js";
import { dedupe, withoutQuotes } from "../utils.js";
function resolveVariants({
variants,
localProviderUrlResolver
}) {
return variants.map((variant) => ({
...variant,
weight: variant.weight?.toString(),
src: variant.src.map((value) => {
const isValue = typeof value === "string" || value instanceof URL;
const url = (isValue ? value : value.url).toString();
const tech = isValue ? void 0 : value.tech;
return {
url: localProviderUrlResolver.resolve(url),
tech
};
})
}));
}
async function resolveFamily({
family,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver
}) {
const name = withoutQuotes(family.name);
const nameWithHash = `${name}-${hasher.hashObject(family)}`;
if (family.provider === LOCAL_PROVIDER_NAME) {
return {
...family,
name,
nameWithHash,
variants: resolveVariants({ variants: family.variants, localProviderUrlResolver }),
fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0
};
}
return {
...family,
name,
nameWithHash,
weights: family.weights ? dedupe(family.weights.map((weight) => weight.toString())) : void 0,
styles: family.styles ? dedupe(family.styles) : void 0,
subsets: family.subsets ? dedupe(family.subsets) : void 0,
fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0,
unicodeRange: family.unicodeRange ? dedupe(family.unicodeRange) : void 0,
// This will be Astro specific eventually
provider: await remoteFontProviderResolver.resolve(family.provider)
};
}
async function resolveFamilies({
families,
...dependencies
}) {
return await Promise.all(
families.map(
(family) => resolveFamily({
family,
...dependencies
})
)
);
}
export {
resolveFamilies,
resolveFamily
};

40
node_modules/astro/dist/assets/fonts/orchestrate.d.ts generated vendored Normal file
View File

@@ -0,0 +1,40 @@
import type { Storage } from 'unstorage';
import type { Logger } from '../../core/logger/core.js';
import type { CssRenderer, FontFileReader, FontMetricsResolver, FontTypeExtractor, Hasher, LocalProviderUrlResolver, RemoteFontProviderResolver, SystemFallbacksProvider, UrlProxy } from './definitions.js';
import type { ConsumableMap, CreateUrlProxyParams, Defaults, FontFamily, FontFileDataMap } from './types.js';
/**
* Manages how fonts are resolved:
*
* - families are resolved
* - unifont providers are extracted from families
* - unifont is initialized
*
* For each family:
* - We create a URL proxy
* - We resolve the font and normalize the result
*
* For each resolved font:
* - We generate the CSS font face
* - We generate optimized fallbacks if applicable
* - We generate CSS variables
*
* Once that's done, the collected data is returned
*/
export declare function orchestrate({ families, hasher, remoteFontProviderResolver, localProviderUrlResolver, storage, cssRenderer, systemFallbacksProvider, fontMetricsResolver, fontTypeExtractor, fontFileReader, logger, createUrlProxy, defaults, }: {
families: Array<FontFamily>;
hasher: Hasher;
remoteFontProviderResolver: RemoteFontProviderResolver;
localProviderUrlResolver: LocalProviderUrlResolver;
storage: Storage;
cssRenderer: CssRenderer;
systemFallbacksProvider: SystemFallbacksProvider;
fontMetricsResolver: FontMetricsResolver;
fontTypeExtractor: FontTypeExtractor;
fontFileReader: FontFileReader;
logger: Logger;
createUrlProxy: (params: CreateUrlProxyParams) => UrlProxy;
defaults: Defaults;
}): Promise<{
fontFileDataMap: FontFileDataMap;
consumableMap: ConsumableMap;
}>;

Some files were not shown because too many files have changed in this diff Show More