Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add experimental responsive images config option #12378

Merged
merged 5 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/astro/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ declare module 'astro:assets' {
getImage: (
options: import('./dist/assets/types.js').UnresolvedImageTransform,
) => Promise<import('./dist/assets/types.js').GetImageResult>;
imageConfig: import('./dist/types/public/config.js').AstroConfig['image'];
imageConfig: import('./dist/types/public/config.js').AstroConfig['image'] & {
experimentalResponsiveImages: boolean;
};
getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
inferRemoteSize: typeof import('./dist/assets/utils/index.js').inferRemoteSize;
Image: typeof import('./components/Image.astro').default;
Expand Down
7 changes: 7 additions & 0 deletions packages/astro/src/assets/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { WithRequired } from '../type-utils.js';
import type { ImageLayout } from '../types/public/index.js';
import type { VALID_INPUT_FORMATS, VALID_OUTPUT_FORMATS } from './consts.js';
import type { ImageService } from './services/service.js';

Expand Down Expand Up @@ -151,6 +152,12 @@ type ImageSharedProps<T> = T & {
* ```
*/
quality?: ImageQuality;

layout?: ImageLayout;

fit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down' | (string & {});

position?: string;
Comment on lines +158 to +160
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone with the shorter names for the HTML attributes for ease of use and avoidance of confusion of camel vs kebab

} & (
| {
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/assets/vite-plugin-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ export default function assets({ settings }: { settings: AstroSettings }): vite.
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `
return /* ts */ `
export { getConfiguredImageService, isLocalService } from "astro/assets";
import { getImage as getImageInternal } from "astro/assets";
export { default as Image } from "astro/components/Image.astro";
export { default as Picture } from "astro/components/Picture.astro";
export { inferRemoteSize } from "astro/assets/utils/inferRemoteSize.js";

export const imageConfig = ${JSON.stringify(settings.config.image)};
export const imageConfig = ${JSON.stringify({ ...settings.config.image, experimentalResponsiveImages: settings.config.experimental.responsiveImages })};
// This is used by the @astrojs/node integration to locate images.
// It's unused on other platforms, but on some platforms like Netlify (and presumably also Vercel)
// new URL("dist/...") is interpreted by the bundler as a signal to include that directory
Expand Down
22 changes: 21 additions & 1 deletion packages/astro/src/core/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const ASTRO_CONFIG_DEFAULTS = {
experimental: {
clientPrerender: false,
contentIntellisense: false,
responsiveImages: false,
},
} satisfies AstroUserConfig & { server: { open: boolean } };

Expand Down Expand Up @@ -284,6 +285,9 @@ export const AstroConfigSchema = z.object({
}),
)
.default([]),
experimentalLayout: z.enum(['responsive', 'fixed', 'full-width', 'none']).optional(),
experimentalObjectFit: z.string().optional(),
experimentalObjectPosition: z.string().optional(),
})
.default(ASTRO_CONFIG_DEFAULTS.image),
devToolbar: z
Expand Down Expand Up @@ -525,6 +529,10 @@ export const AstroConfigSchema = z.object({
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.contentIntellisense),
responsiveImages: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.responsiveImages),
ascorbic marked this conversation as resolved.
Show resolved Hide resolved
})
.strict(
`Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.`,
Expand Down Expand Up @@ -688,7 +696,7 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
'The value of `outDir` must not point to a path within the folder set as `publicDir`, this will cause an infinite loop',
})
.superRefine((configuration, ctx) => {
const { site, i18n, output } = configuration;
const { site, i18n, output, image, experimental } = configuration;
const hasDomains = i18n?.domains ? Object.keys(i18n.domains).length > 0 : false;
if (hasDomains) {
if (!site) {
Expand All @@ -705,6 +713,18 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
});
}
}
if (
!experimental.responsiveImages &&
(image.experimentalLayout ||
image.experimentalObjectFit ||
image.experimentalObjectPosition)
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
'The `experimentalLayout`, `experimentalObjectFit`, and `experimentalObjectPosition` options are only available when `experimental.responsiveImages` is enabled.',
});
}
});

return AstroConfigRelativeSchema;
Expand Down
66 changes: 66 additions & 0 deletions packages/astro/src/types/public/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,37 @@ export interface ViteUserConfig extends OriginalViteUserConfig {

*/
remotePatterns?: Partial<RemotePattern>[];

/**
* @docs
* @name image.experimentalLayout
* @default `undefined`
* @description
* The default layout type for responsive images. Can be overridden by the `layout` prop on the image component.
* Requires the `experimental.responsiveImages` flag to be enabled.
* - `responsive` - The image will scale to fit the container, maintaining its aspect ratio, but will not exceed the specified dimensions.
* - `fixed` - The image will maintain its original dimensions.
* - `full-width` - The image will scale to fit the container, maintaining its aspect ratio.
*/
experimentalLayout?: ImageLayout | undefined;
/**
* @docs
* @name image.experimentalObjectFit
* @default `"cover"`
* @description
* The default object-fit value for responsive images. Can be overridden by the `fit` prop on the image component.
* Requires the `experimental.responsiveImages` flag to be enabled.
*/
experimentalObjectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down' | (string & {});
/**
* @docs
* @name image.experimentalObjectPosition
* @default `"center"`
* @description
* The default object-position value for responsive images. Can be overridden by the `position` prop on the image component.
* Requires the `experimental.responsiveImages` flag to be enabled.
*/
experimentalObjectPosition?: string;
};

/**
Expand Down Expand Up @@ -1699,9 +1730,44 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
* To use this feature with the Astro VS Code extension, you must also enable the `astro.content-intellisense` option in your VS Code settings. For editors using the Astro language server directly, pass the `contentIntellisense: true` initialization parameter to enable this feature.
*/
contentIntellisense?: boolean;

/**
* @docs
* @name experimental.responsiveImages
* @type {boolean}
* @default `undefined`
* @version 5.0.0
* @description
*
* Enables and configures automatic responsive image options for images in your project. Set to `true` (for no default option passed to your images) or an object with default responsive image configuration options.
*
* ```js
* {
* experimental: {
* responsiveImages: {
* layout: 'responsive',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now changes in that you only set this to true and now you go to regular image config to configure the properties, right?

And, since we now have multiple properties (acknowledging there may still even be more to come!), when you do add the regular image config for this example, I would set at least a couple of them.

(Not meaning to overly edit while stuff is still happening, just didn't want us to forget!)

* },
* }
* ```
*
ascorbic marked this conversation as resolved.
Show resolved Hide resolved
* Then, you can add a `layout` option to any `<Image />` component when needed to override your default configuration: `responsive`, `fixed`, `full-width`, or `none`. This attribute is required to transform your images if `responsiveImages.layout` is not configured. Images with a layout value of `undefined` or `none` will not be transformed.
*
* ```astro
* ---
* import { Image } from 'astro:assets';
* import myImage from '../assets/my_image.png';
* ---
* <Image src={myImage} alt="A description of my image." layout='fixed' />
* ```
*
*/

responsiveImages?: boolean;
};
}

export type ImageLayout = 'responsive' | 'fixed' | 'full-width' | 'none';

/**
* Resolved Astro Config
*
Expand Down
Loading