Fixes type definitions @astrojs/image
and adds more documentation to the README (#4045)
* WIP: moving to a static .d.ts types file * fixing named exports for getImage and getPicture * removing the exports.astro map for now * WIP: adding readme docs for component attributes * Adding docs for getImage and getPicture * leaning fully on TSC to build .d.ts files * finally found the solution for proper ESM import types * adding a note to the README for tsconfig updates * chore: add changesets * typo * docs: removing the "Images in Markdown" example * removing the need for publishing src to NPM * fix: make type re-export explicit * updating image module defs to match InputFormat * using astro syntax highlighting for README code blocks * nit: missing backtick in README * make sure Astro component directives aren't recommended twice
This commit is contained in:
parent
55c8aced44
commit
a397b981f5
22 changed files with 606 additions and 363 deletions
5
.changeset/fifty-apricots-clap.md
Normal file
5
.changeset/fifty-apricots-clap.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Adding support for custom "astro/client" type definitions in `@astrojs/image`
|
5
.changeset/five-singers-notice.md
Normal file
5
.changeset/five-singers-notice.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/image': minor
|
||||
---
|
||||
|
||||
Big improvements to the TypeScript and Language Tools support for `@astrojs/image` :tada:
|
173
packages/astro/client-base.d.ts
vendored
Normal file
173
packages/astro/client-base.d.ts
vendored
Normal file
|
@ -0,0 +1,173 @@
|
|||
/// <reference types="vite/types/importMeta" />
|
||||
|
||||
// CSS modules
|
||||
type CSSModuleClasses = { readonly [key: string]: string };
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.scss' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.sass' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.less' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.styl' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.stylus' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.pcss' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
|
||||
// CSS
|
||||
declare module '*.css' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.scss' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.sass' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.less' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.styl' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.stylus' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.pcss' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
|
||||
// Built-in asset types
|
||||
// see `src/constants.ts`
|
||||
|
||||
// media
|
||||
declare module '*.mp4' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.webm' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.ogg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.mp3' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.wav' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.flac' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.aac' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// fonts
|
||||
declare module '*.woff' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.woff2' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.eot' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.ttf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.otf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// other
|
||||
declare module '*.wasm' {
|
||||
const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>;
|
||||
export default initWasm;
|
||||
}
|
||||
declare module '*.webmanifest' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.pdf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.txt' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// web worker
|
||||
declare module '*?worker' {
|
||||
const workerConstructor: {
|
||||
new (): Worker;
|
||||
};
|
||||
export default workerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?worker&inline' {
|
||||
const workerConstructor: {
|
||||
new (): Worker;
|
||||
};
|
||||
export default workerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?sharedworker' {
|
||||
const sharedWorkerConstructor: {
|
||||
new (): SharedWorker;
|
||||
};
|
||||
export default sharedWorkerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?raw' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?url' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?inline' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
174
packages/astro/client.d.ts
vendored
174
packages/astro/client.d.ts
vendored
|
@ -1,69 +1,4 @@
|
|||
/// <reference types="vite/types/importMeta" />
|
||||
|
||||
// CSS modules
|
||||
type CSSModuleClasses = { readonly [key: string]: string };
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.scss' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.sass' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.less' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.styl' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.stylus' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.pcss' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
|
||||
// CSS
|
||||
declare module '*.css' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.scss' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.sass' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.less' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.styl' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.stylus' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
declare module '*.pcss' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
|
||||
// Built-in asset types
|
||||
// see `src/constants.ts`
|
||||
/// <reference path="./client-base.d.ts" />
|
||||
|
||||
// images
|
||||
declare module '*.jpg' {
|
||||
|
@ -98,110 +33,3 @@ declare module '*.avif' {
|
|||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// media
|
||||
declare module '*.mp4' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.webm' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.ogg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.mp3' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.wav' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.flac' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.aac' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// fonts
|
||||
declare module '*.woff' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.woff2' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.eot' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.ttf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.otf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// other
|
||||
declare module '*.wasm' {
|
||||
const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>;
|
||||
export default initWasm;
|
||||
}
|
||||
declare module '*.webmanifest' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.pdf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.txt' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// web worker
|
||||
declare module '*?worker' {
|
||||
const workerConstructor: {
|
||||
new (): Worker;
|
||||
};
|
||||
export default workerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?worker&inline' {
|
||||
const workerConstructor: {
|
||||
new (): Worker;
|
||||
};
|
||||
export default workerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?sharedworker' {
|
||||
const sharedWorkerConstructor: {
|
||||
new (): SharedWorker;
|
||||
};
|
||||
export default sharedWorkerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?raw' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?url' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?inline' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
".": "./astro.js",
|
||||
"./env": "./env.d.ts",
|
||||
"./client": "./client.d.ts",
|
||||
"./client-base": "./client-base.d.ts",
|
||||
"./astro-jsx": "./astro-jsx.d.ts",
|
||||
"./jsx/*": "./dist/jsx/*",
|
||||
"./jsx-runtime": "./dist/jsx-runtime/index.js",
|
||||
|
@ -65,6 +66,7 @@
|
|||
"config.mjs",
|
||||
"env.d.ts",
|
||||
"client.d.ts",
|
||||
"client-base.d.ts",
|
||||
"astro-jsx.d.ts",
|
||||
"README.md",
|
||||
"vendor"
|
||||
|
|
|
@ -63,11 +63,205 @@ export default {
|
|||
Then, restart the dev server.
|
||||
</details>
|
||||
|
||||
### Update `tsconfig.json`
|
||||
|
||||
For the best development experience, add the integrations type definitions to your project's `tsconfig.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Replace `astro/client` with `@astrojs/image/client`
|
||||
"types": ["@astrojs/image/client"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The included `sharp` transformer supports resizing images and encoding them to different image formats. Third-party image services will be able to add support for custom transformations as well (ex: `blur`, `filter`, `rotate`, etc).
|
||||
|
||||
### `<Image />`
|
||||
|
||||
The built-in `<Image />` component is used to create an optimized `<img />` for both remote images hosted on other domains as well as local images imported from your project's `src` directory.
|
||||
|
||||
The included `sharp` transformer supports resizing images and encoding them to different image formats. Third-party image services will be able to add support for custom transformations as well (ex: `blur`, `filter`, `rotate`, etc).
|
||||
In addition to the component-specific properties, any valid HTML attribute for the `<img />` included in the `<Image />` component will be included in the built `<img />`.
|
||||
|
||||
#### src
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `string` | `ImageMetadata` | `Promise<ImageMetadata>`<br>
|
||||
**Required:** `true`
|
||||
</p>
|
||||
|
||||
Source for the original image file.
|
||||
|
||||
For images in your project's repository, use the `src` relative to the `public` directory. For remote images, provide the full URL.
|
||||
|
||||
#### format
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** 'avif' | 'jpeg' | 'png' | 'webp'<br>
|
||||
**Default:** `undefined`
|
||||
</p>
|
||||
|
||||
The output format to be used in the optimized image. The original image format will be used if `format` is not provided.
|
||||
|
||||
#### quality
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `number`<br>
|
||||
**Default:** `undefined`
|
||||
</p>
|
||||
|
||||
The compression quality used during optimization. The image service will use a default quality if not provided.
|
||||
|
||||
#### width
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `number`<br>
|
||||
**Default:** `undefined`
|
||||
</p>
|
||||
|
||||
The desired width of the output image. Combine with `height` to crop the image to an exact size, or `aspectRatio` to automatically calculate and crop the height.
|
||||
|
||||
Dimensions are optional for local images, the original image size will be used if not provided.
|
||||
|
||||
For remote images, the integration needs to be able to calculate dimensions for the optimized image. This can be done by providing `width` and `height` or by providing one dimension and an `aspectRatio`.
|
||||
|
||||
#### height
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `number`<br>
|
||||
**Default:** `undefined`
|
||||
</p>
|
||||
|
||||
The desired height of the output image. Combine with `width` to crop the image to an exact size, or `aspectRatio` to automatically calculate and crop the width.
|
||||
|
||||
Dimensions are optional for local images, the original image size will be used if not provided.
|
||||
|
||||
For remote images, the integration needs to be able to calculate dimensions for the optimized image. This can be done by providing `width` and `height` or by providing one dimension and an `aspectRatio`.
|
||||
|
||||
#### aspectRatio
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `number` | `string`<br>
|
||||
**Default:** `undefined`
|
||||
</p>
|
||||
|
||||
The desired aspect ratio of the output image. Combine with either `width` or `height` to automatically calculate and crop the other dimension.
|
||||
|
||||
A `string` can be provided in the form of `{width}:{height}`, ex: `16:9` or `3:4`.
|
||||
|
||||
A `number` can also be provided, useful when the aspect ratio is calculated at build time. This can be an inline number such as `1.777` or inlined as a JSX expression like `aspectRatio={16/9}`.
|
||||
|
||||
### `<Picture /`>
|
||||
|
||||
#### src
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `string` | `ImageMetadata` | `Promise<ImageMetadata>`<br>
|
||||
**Required:** `true`
|
||||
</p>
|
||||
|
||||
Source for the original image file.
|
||||
|
||||
For images in your project's repository, use the `src` relative to the `public` directory. For remote images, provide the full URL.
|
||||
|
||||
#### alt
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `string`<br>
|
||||
**Default:** `undefined`
|
||||
</p>
|
||||
|
||||
If provided, the `alt` string will be included on the built `<img />` element.
|
||||
|
||||
#### sizes
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `string`<br>
|
||||
**Required:** `true`
|
||||
</p>
|
||||
|
||||
The HTMLImageElement property `sizes` allows you to specify the layout width of the image for each of a list of media conditions.
|
||||
|
||||
See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/sizes) for more details.
|
||||
|
||||
#### widths
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `number[]`<br>
|
||||
**Reuqired:** `true`
|
||||
</p>
|
||||
|
||||
The list of sizes that should be built for responsive images. This is combined with `aspectRatio` to calculate the final dimensions of each built image.
|
||||
|
||||
```astro
|
||||
// Builds three images: 400x400, 800x800, and 1200x1200
|
||||
<Picture src={...} widths={[400, 800, 1200]} aspectRatio="1:1" />
|
||||
```
|
||||
|
||||
#### aspectRatio
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** `number` | `string`<br>
|
||||
**Required:** `true`
|
||||
</p>
|
||||
|
||||
The desired aspect ratio of the output image. This is combined with `widhts` to calculate the final dimensions of each built image.
|
||||
|
||||
A `string` can be provided in the form of `{width}:{height}`, ex: `16:9` or `3:4`.
|
||||
|
||||
A `number` can also be provided, useful when the aspect ratio is calculated at build time. This can be an inline number such as `1.777` or inlined as a JSX expression like `aspectRatio={16/9}`.
|
||||
|
||||
#### formats
|
||||
|
||||
<p>
|
||||
|
||||
**Type:** Array<'avif' | 'jpeg' | 'png' | 'webp'><br>
|
||||
**Default:** `undefined`
|
||||
</p>
|
||||
|
||||
The output formats to be used in the optimized image. If not provided, `webp` and `avif` will be used in addition to the original image format.
|
||||
|
||||
### `getImage`
|
||||
|
||||
This is the helper function used by the `<Image />` component to build `<img />` attributes for the transformed image. This helper can be used directly for more complex use cases that aren't currently supported by the `<Image />` component.
|
||||
|
||||
This helper takes in an object with the same properties as the `<Image />` component and returns an object with attributes that should be included on the final `<img />` element.
|
||||
|
||||
This can helpful if you need to add preload links to a page's `<head>`.
|
||||
|
||||
```astro
|
||||
---
|
||||
import { getImage } from '@astrojs/image';
|
||||
|
||||
const { src } = await getImage('../assets/hero.png');
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<link rel="preload" as="image" href={src}>
|
||||
</head>
|
||||
</html>
|
||||
```
|
||||
|
||||
### `getPicture`
|
||||
|
||||
This is the helper function used by the `<Picture />` component to build multiple sizes and formats for responsive images. This helper can be used directly for more complex use cases that aren't currently supported by the `<Picture />` component.
|
||||
|
||||
This helper takes in an object with the same properties as the `<Picture />` component and returns an object attributes that should be included on the final `<img />` element **and** a list of sources that should be used to render all `<source>`s for the `<picture>` element.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -102,7 +296,7 @@ export default {
|
|||
|
||||
Image files in your project's `src` directory can be imported in frontmatter and passed directly to the `<Image />` component. All other properties are optional and will default to the original image file's properties if not provided.
|
||||
|
||||
```html
|
||||
```astro
|
||||
---
|
||||
import { Image } from '@astrojs/image/components';
|
||||
import heroImage from '../assets/hero.png';
|
||||
|
@ -130,7 +324,7 @@ import heroImage from '../assets/hero.png';
|
|||
|
||||
Remote images can be transformed with the `<Image />` component. The `<Image />` component needs to know the final dimensions for the `<img />` element to avoid content layout shifts. For remote images, this means you must either provide `width` and `height`, or one of the dimensions plus the required `aspectRatio`.
|
||||
|
||||
```html
|
||||
```astro
|
||||
---
|
||||
import { Image } from '@astrojs/image/components';
|
||||
|
||||
|
@ -148,28 +342,6 @@ const imageUrl = 'https://www.google.com/images/branding/googlelogo/2x/googlelog
|
|||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Images in markdown</strong></summary>
|
||||
|
||||
The `<Image />` component can also be used to optimize images in markdown pages. For local images imported from your project's `src` directory, use Astro's the `setup` frontmatter to import the image file.
|
||||
|
||||
```html
|
||||
---
|
||||
setup: |
|
||||
import { Image } from '@astrojs/image/components'
|
||||
import hero from '../../assets/blog/introducing-astro.jpg'
|
||||
title: Hello world!
|
||||
publishDate: 12 Sep 2021
|
||||
name: Nate Moore
|
||||
value: 128
|
||||
description: Just a Hello World Post!
|
||||
---
|
||||
|
||||
<Image src={hero} width={640} />
|
||||
<Image src="https://example.com/image.jpg" width={640} aspectRatio="16:9" />
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Responsive pictures</strong></summary>
|
||||
|
||||
|
@ -179,7 +351,7 @@ description: Just a Hello World Post!
|
|||
|
||||
For remote images, an `aspectRatio` is required to ensure the correct `height` can be calculated at build time.
|
||||
|
||||
```html
|
||||
```astro
|
||||
---
|
||||
import { Picture } from '@astrojs/image/components';
|
||||
import hero from '../assets/hero.png';
|
||||
|
|
55
packages/integrations/image/client.d.ts
vendored
Normal file
55
packages/integrations/image/client.d.ts
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
type InputFormat =
|
||||
| 'avif'
|
||||
| 'gif'
|
||||
| 'heic'
|
||||
| 'heif'
|
||||
| 'jpeg'
|
||||
| 'jpg'
|
||||
| 'png'
|
||||
| 'tiff'
|
||||
| 'webp';
|
||||
|
||||
interface ImageMetadata {
|
||||
src: string;
|
||||
width: number;
|
||||
height: number;
|
||||
format: InputFormat;
|
||||
}
|
||||
|
||||
// images
|
||||
declare module '*.avif' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.gif' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.heic' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.heif' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.jpeg' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.jpg' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.png' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.tiff' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
||||
declare module '*.webp' {
|
||||
const metadata: ImageMetadata;
|
||||
export default metadata;
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
---
|
||||
// @ts-ignore
|
||||
import { getImage } from '../dist/index.js';
|
||||
import type { ImageAttributes, ImageMetadata, TransformOptions, OutputFormat } from '../dist/types';
|
||||
import type { ImgHTMLAttributes } from './index.js';
|
||||
import type { ImageMetadata, TransformOptions, OutputFormat } from '../dist/index.js';
|
||||
|
||||
export interface LocalImageProps extends Omit<TransformOptions, 'src'>, Omit<ImageAttributes, 'src' | 'width' | 'height'> {
|
||||
interface LocalImageProps extends
|
||||
Omit<TransformOptions, 'src'>,
|
||||
Omit<ImgHTMLAttributes, | 'src' | 'width' | 'height'> {
|
||||
src: ImageMetadata | Promise<{ default: ImageMetadata }>;
|
||||
}
|
||||
|
||||
export interface RemoteImageProps extends TransformOptions, ImageAttributes {
|
||||
interface RemoteImageProps extends TransformOptions, astroHTML.JSX.ImgHTMLAttributes {
|
||||
src: string;
|
||||
format: OutputFormat;
|
||||
width: number;
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
---
|
||||
import { getPicture } from '../dist/index.js';
|
||||
import type { ImageAttributes, ImageMetadata, OutputFormat, PictureAttributes, TransformOptions } from '../dist/types';
|
||||
import type { ImgHTMLAttributes, HTMLAttributes } from './index.js';
|
||||
import type { ImageMetadata, OutputFormat, TransformOptions } from '../dist/index.js';
|
||||
|
||||
export interface LocalImageProps extends Omit<PictureAttributes, 'src' | 'width' | 'height'>, Omit<TransformOptions, 'src'>, Pick<ImageAttributes, 'loading' | 'decoding'> {
|
||||
interface LocalImageProps extends
|
||||
Omit<HTMLAttributes, 'src' | 'width' | 'height'>,
|
||||
Omit<TransformOptions, 'src'>,
|
||||
Pick<astroHTML.JSX.ImgHTMLAttributes, 'loading' | 'decoding'> {
|
||||
src: ImageMetadata | Promise<{ default: ImageMetadata }>;
|
||||
alt?: string;
|
||||
sizes: HTMLImageElement['sizes'];
|
||||
|
@ -10,7 +14,10 @@ export interface LocalImageProps extends Omit<PictureAttributes, 'src' | 'width'
|
|||
formats?: OutputFormat[];
|
||||
}
|
||||
|
||||
export interface RemoteImageProps extends Omit<PictureAttributes, 'src' | 'width' | 'height'>, TransformOptions, Pick<ImageAttributes, 'loading' | 'decoding'> {
|
||||
interface RemoteImageProps extends
|
||||
Omit<HTMLAttributes, 'src' | 'width' | 'height'>,
|
||||
TransformOptions,
|
||||
Pick<ImgHTMLAttributes, 'loading' | 'decoding'> {
|
||||
src: string;
|
||||
alt?: string;
|
||||
sizes: HTMLImageElement['sizes'];
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
/// <reference types="astro/astro-jsx" />
|
||||
export { default as Image } from './Image.astro';
|
||||
export { default as Picture } from './Picture.astro';
|
||||
|
||||
// TODO: should these directives be removed from astroHTML.JSX?
|
||||
export type ImgHTMLAttributes = Omit<astroHTML.JSX.ImgHTMLAttributes, 'client:list' | 'set:text' | 'set:html' | 'is:raw'>;
|
||||
export type HTMLAttributes = Omit<astroHTML.JSX.HTMLAttributes, 'client:list' | 'set:text' | 'set:html' | 'is:raw'>;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"description": "Load and transform images in your Astro site.",
|
||||
"version": "0.2.0",
|
||||
"type": "module",
|
||||
"types": "./dist/types.d.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"author": "withastro",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
@ -20,21 +20,18 @@
|
|||
"bugs": "https://github.com/withastro/astro/issues",
|
||||
"homepage": "https://docs.astro.build/en/guides/integrations-guide/image/",
|
||||
"exports": {
|
||||
".": {
|
||||
"astro": "./components/index.js",
|
||||
"import": "./dist/index.js"
|
||||
},
|
||||
".": "./dist/index.js",
|
||||
"./sharp": "./dist/loaders/sharp.js",
|
||||
"./endpoints/dev": "./dist/endpoints/dev.js",
|
||||
"./endpoints/prod": "./dist/endpoints/prod.js",
|
||||
"./components": "./components/index.js",
|
||||
"./package.json": "./package.json"
|
||||
"./package.json": "./package.json",
|
||||
"./client": "./client.d.ts"
|
||||
},
|
||||
"files": [
|
||||
"components",
|
||||
"dist",
|
||||
"src",
|
||||
"types"
|
||||
"client.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
|
||||
|
|
|
@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
|
|||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { OUTPUT_DIR } from '../constants.js';
|
||||
import type { SSRImageService, TransformOptions } from '../types.js';
|
||||
import type { SSRImageService, TransformOptions } from '../loaders/index.js';
|
||||
import { isRemoteImage, loadLocalImage, loadRemoteImage } from '../utils/images.js';
|
||||
import { ensureDir } from '../utils/paths.js';
|
||||
|
||||
|
|
|
@ -1,5 +1,116 @@
|
|||
import integration from './integration.js';
|
||||
export * from './lib/get-image.js';
|
||||
export * from './lib/get-picture.js';
|
||||
import type { AstroConfig, AstroIntegration } from 'astro';
|
||||
import { ssgBuild } from './build/ssg.js';
|
||||
import { ssrBuild } from './build/ssr.js';
|
||||
import { PKG_NAME, ROUTE_PATTERN } from './constants.js';
|
||||
import { ImageService, TransformOptions } from './loaders/index.js';
|
||||
import { filenameFormat, propsToFilename } from './utils/paths.js';
|
||||
import { createPlugin } from './vite-plugin-astro-image.js';
|
||||
|
||||
export default integration;
|
||||
export { getImage } from './lib/get-image.js';
|
||||
export { getPicture } from './lib/get-picture.js';
|
||||
export * from './loaders/index.js';
|
||||
export type { ImageMetadata} from './vite-plugin-astro-image.js';
|
||||
|
||||
interface ImageIntegration {
|
||||
loader?: ImageService;
|
||||
addStaticImage?: (transform: TransformOptions) => void;
|
||||
filenameFormat?: (transform: TransformOptions, searchParams: URLSearchParams) => string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var astroImage: ImageIntegration | undefined;
|
||||
}
|
||||
|
||||
export interface IntegrationOptions {
|
||||
/**
|
||||
* Entry point for the @type {HostedImageService} or @type {LocalImageService} to be used.
|
||||
*/
|
||||
serviceEntryPoint?: string;
|
||||
}
|
||||
|
||||
export default function integration(options: IntegrationOptions = {}): AstroIntegration {
|
||||
const resolvedOptions = {
|
||||
serviceEntryPoint: '@astrojs/image/sharp',
|
||||
...options,
|
||||
};
|
||||
|
||||
// During SSG builds, this is used to track all transformed images required.
|
||||
const staticImages = new Map<string, Map<string, TransformOptions>>();
|
||||
|
||||
let _config: AstroConfig;
|
||||
let output: 'server' | 'static';
|
||||
|
||||
function getViteConfiguration() {
|
||||
return {
|
||||
plugins: [createPlugin(_config, resolvedOptions)],
|
||||
optimizeDeps: {
|
||||
include: ['image-size', 'sharp'],
|
||||
},
|
||||
ssr: {
|
||||
noExternal: ['@astrojs/image', resolvedOptions.serviceEntryPoint],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: PKG_NAME,
|
||||
hooks: {
|
||||
'astro:config:setup': ({ command, config, injectRoute, updateConfig }) => {
|
||||
_config = config;
|
||||
|
||||
// Always treat `astro dev` as SSR mode, even without an adapter
|
||||
output = command === 'dev' ? 'server' : config.output;
|
||||
|
||||
updateConfig({ vite: getViteConfiguration() });
|
||||
|
||||
if (output === 'server') {
|
||||
injectRoute({
|
||||
pattern: ROUTE_PATTERN,
|
||||
entryPoint:
|
||||
command === 'dev' ? '@astrojs/image/endpoints/dev' : '@astrojs/image/endpoints/prod',
|
||||
});
|
||||
}
|
||||
},
|
||||
'astro:server:setup': async () => {
|
||||
globalThis.astroImage = {};
|
||||
},
|
||||
'astro:build:setup': () => {
|
||||
// Used to cache all images rendered to HTML
|
||||
// Added to globalThis to share the same map in Node and Vite
|
||||
function addStaticImage(transform: TransformOptions) {
|
||||
const srcTranforms = staticImages.has(transform.src)
|
||||
? staticImages.get(transform.src)!
|
||||
: new Map<string, TransformOptions>();
|
||||
|
||||
srcTranforms.set(propsToFilename(transform), transform);
|
||||
|
||||
staticImages.set(transform.src, srcTranforms);
|
||||
}
|
||||
|
||||
// Helpers for building static images should only be available for SSG
|
||||
globalThis.astroImage =
|
||||
output === 'static'
|
||||
? {
|
||||
addStaticImage,
|
||||
filenameFormat,
|
||||
}
|
||||
: {};
|
||||
},
|
||||
'astro:build:done': async ({ dir }) => {
|
||||
if (output === 'server') {
|
||||
// for SSR builds, copy all image files from src to dist
|
||||
// to make sure they are available for use in production
|
||||
await ssrBuild({ srcDir: _config.srcDir, outDir: dir });
|
||||
} else {
|
||||
// for SSG builds, build all requested image transforms to dist
|
||||
const loader = globalThis?.astroImage?.loader;
|
||||
|
||||
if (loader && 'transform' in loader && staticImages.size > 0) {
|
||||
await ssgBuild({ loader, staticImages, srcDir: _config.srcDir, outDir: dir });
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
import type { AstroConfig, AstroIntegration } from 'astro';
|
||||
import { ssgBuild } from './build/ssg.js';
|
||||
import { ssrBuild } from './build/ssr.js';
|
||||
import { PKG_NAME, ROUTE_PATTERN } from './constants.js';
|
||||
import { IntegrationOptions, TransformOptions } from './types.js';
|
||||
import { filenameFormat, propsToFilename } from './utils/paths.js';
|
||||
import { createPlugin } from './vite-plugin-astro-image.js';
|
||||
|
||||
export default function integration(options: IntegrationOptions = {}): AstroIntegration {
|
||||
const resolvedOptions = {
|
||||
serviceEntryPoint: '@astrojs/image/sharp',
|
||||
...options,
|
||||
};
|
||||
|
||||
// During SSG builds, this is used to track all transformed images required.
|
||||
const staticImages = new Map<string, Map<string, TransformOptions>>();
|
||||
|
||||
let _config: AstroConfig;
|
||||
let output: 'server' | 'static';
|
||||
|
||||
function getViteConfiguration() {
|
||||
return {
|
||||
plugins: [createPlugin(_config, resolvedOptions)],
|
||||
optimizeDeps: {
|
||||
include: ['image-size', 'sharp'],
|
||||
},
|
||||
ssr: {
|
||||
noExternal: ['@astrojs/image', resolvedOptions.serviceEntryPoint],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: PKG_NAME,
|
||||
hooks: {
|
||||
'astro:config:setup': ({ command, config, injectRoute, updateConfig }) => {
|
||||
_config = config;
|
||||
|
||||
// Always treat `astro dev` as SSR mode, even without an adapter
|
||||
output = command === 'dev' ? 'server' : config.output;
|
||||
|
||||
updateConfig({ vite: getViteConfiguration() });
|
||||
|
||||
if (output === 'server') {
|
||||
injectRoute({
|
||||
pattern: ROUTE_PATTERN,
|
||||
entryPoint:
|
||||
command === 'dev' ? '@astrojs/image/endpoints/dev' : '@astrojs/image/endpoints/prod',
|
||||
});
|
||||
}
|
||||
},
|
||||
'astro:server:setup': async () => {
|
||||
globalThis.astroImage = {};
|
||||
},
|
||||
'astro:build:setup': () => {
|
||||
// Used to cache all images rendered to HTML
|
||||
// Added to globalThis to share the same map in Node and Vite
|
||||
function addStaticImage(transform: TransformOptions) {
|
||||
const srcTranforms = staticImages.has(transform.src)
|
||||
? staticImages.get(transform.src)!
|
||||
: new Map<string, TransformOptions>();
|
||||
|
||||
srcTranforms.set(propsToFilename(transform), transform);
|
||||
|
||||
staticImages.set(transform.src, srcTranforms);
|
||||
}
|
||||
|
||||
// Helpers for building static images should only be available for SSG
|
||||
globalThis.astroImage =
|
||||
output === 'static'
|
||||
? {
|
||||
addStaticImage,
|
||||
filenameFormat,
|
||||
}
|
||||
: {};
|
||||
},
|
||||
'astro:build:done': async ({ dir }) => {
|
||||
if (output === 'server') {
|
||||
// for SSR builds, copy all image files from src to dist
|
||||
// to make sure they are available for use in production
|
||||
await ssrBuild({ srcDir: _config.srcDir, outDir: dir });
|
||||
} else {
|
||||
// for SSG builds, build all requested image transforms to dist
|
||||
const loader = globalThis?.astroImage?.loader;
|
||||
|
||||
if (loader && 'transform' in loader && staticImages.size > 0) {
|
||||
await ssgBuild({ loader, staticImages, srcDir: _config.srcDir, outDir: dir });
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
/// <reference types="astro/astro-jsx" />
|
||||
import slash from 'slash';
|
||||
import { ROUTE_PATTERN } from '../constants.js';
|
||||
import sharp from '../loaders/sharp.js';
|
||||
import {
|
||||
ImageAttributes,
|
||||
ImageMetadata,
|
||||
ImageService,
|
||||
isSSRService,
|
||||
OutputFormat,
|
||||
TransformOptions,
|
||||
} from '../types.js';
|
||||
} from '../loaders/index.js';
|
||||
import { isRemoteImage, parseAspectRatio } from '../utils/images.js';
|
||||
import { ImageMetadata } from '../vite-plugin-astro-image.js';
|
||||
|
||||
export interface GetImageTransform extends Omit<TransformOptions, 'src'> {
|
||||
src: string | ImageMetadata | Promise<{ default: ImageMetadata }>;
|
||||
|
@ -101,7 +101,7 @@ async function resolveTransform(input: GetImageTransform): Promise<TransformOpti
|
|||
* @param transform @type {TransformOptions} The transformations requested for the optimized image.
|
||||
* @returns @type {ImageAttributes} The HTML attributes to be included on the built `<img />` element.
|
||||
*/
|
||||
export async function getImage(transform: GetImageTransform): Promise<ImageAttributes> {
|
||||
export async function getImage(transform: GetImageTransform): Promise<astroHTML.JSX.ImgHTMLAttributes> {
|
||||
if (!transform.src) {
|
||||
throw new Error('[@astrojs/image] `src` is required');
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
/// <reference types="astro/astro-jsx" />
|
||||
import { lookup } from 'mrmime';
|
||||
import { extname } from 'node:path';
|
||||
import { ImageAttributes, ImageMetadata, OutputFormat, TransformOptions } from '../types.js';
|
||||
import { OutputFormat, TransformOptions } from '../loaders/index.js';
|
||||
import { parseAspectRatio } from '../utils/images.js';
|
||||
import { ImageMetadata } from '../vite-plugin-astro-image.js';
|
||||
import { getImage } from './get-image.js';
|
||||
|
||||
export interface GetPictureParams {
|
||||
|
@ -12,7 +14,7 @@ export interface GetPictureParams {
|
|||
}
|
||||
|
||||
export interface GetPictureResult {
|
||||
image: ImageAttributes;
|
||||
image: astroHTML.JSX.HTMLAttributes;
|
||||
sources: { type: string; srcset: string }[];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,4 @@
|
|||
/// <reference types="astro/astro-jsx" />
|
||||
export * from './index.js';
|
||||
|
||||
interface ImageIntegration {
|
||||
loader?: ImageService;
|
||||
addStaticImage?: (transform: TransformOptions) => void;
|
||||
filenameFormat?: (transform: TransformOptions, searchParams: URLSearchParams) => string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var astroImage: ImageIntegration | undefined;
|
||||
}
|
||||
|
||||
export type InputFormat =
|
||||
| 'heic'
|
||||
| 'heif'
|
||||
|
@ -25,21 +12,6 @@ export type InputFormat =
|
|||
|
||||
export type OutputFormat = 'avif' | 'jpeg' | 'png' | 'webp';
|
||||
|
||||
/**
|
||||
* Converts a set of image transforms to the filename to use when building for static.
|
||||
*
|
||||
* This is only used for static production builds and ignored when an SSR adapter is used,
|
||||
* or in `astro dev` for static builds.
|
||||
*/
|
||||
export type FilenameFormatter = (transform: TransformOptions) => string;
|
||||
|
||||
export interface IntegrationOptions {
|
||||
/**
|
||||
* Entry point for the @type {HostedImageService} or @type {LocalImageService} to be used.
|
||||
*/
|
||||
serviceEntryPoint?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the original image and transforms that need to be applied to it.
|
||||
*/
|
||||
|
@ -83,22 +55,19 @@ export interface TransformOptions {
|
|||
aspectRatio?: number | `${number}:${number}`;
|
||||
}
|
||||
|
||||
export type ImageAttributes = astroHTML.JSX.ImgHTMLAttributes;
|
||||
export type PictureAttributes = astroHTML.JSX.HTMLAttributes;
|
||||
|
||||
export interface HostedImageService<T extends TransformOptions = TransformOptions> {
|
||||
/**
|
||||
* Gets the HTML attributes needed for the server rendered `<img />` element.
|
||||
*/
|
||||
getImageAttributes(transform: T): Promise<ImageAttributes>;
|
||||
getImageAttributes(transform: T): Promise<astroHTML.JSX.ImgHTMLAttributes>;
|
||||
}
|
||||
|
||||
export interface SSRImageService<T extends TransformOptions = TransformOptions>
|
||||
extends HostedImageService<T> {
|
||||
/**
|
||||
* Gets tthe HTML attributes needed for the server rendered `<img />` element.
|
||||
* Gets the HTML attributes needed for the server rendered `<img />` element.
|
||||
*/
|
||||
getImageAttributes(transform: T): Promise<Exclude<ImageAttributes, 'src'>>;
|
||||
getImageAttributes(transform: T): Promise<Exclude<astroHTML.JSX.ImgHTMLAttributes, 'src'>>;
|
||||
/**
|
||||
* Serializes image transformation properties to URLSearchParams, used to build
|
||||
* the final `src` that points to the self-hosted SSR endpoint.
|
||||
|
@ -134,10 +103,3 @@ export function isHostedService(service: ImageService): service is ImageService
|
|||
export function isSSRService(service: ImageService): service is SSRImageService {
|
||||
return 'transform' in service;
|
||||
}
|
||||
|
||||
export interface ImageMetadata {
|
||||
src: string;
|
||||
width: number;
|
||||
height: number;
|
||||
format: InputFormat;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import sharp from 'sharp';
|
||||
import type { OutputFormat, SSRImageService, TransformOptions } from '../types.js';
|
||||
import type { OutputFormat, SSRImageService, TransformOptions } from './index.js';
|
||||
import { isAspectRatioString, isOutputFormat } from '../utils/images.js';
|
||||
|
||||
class SharpService implements SSRImageService {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import fs from 'node:fs/promises';
|
||||
import type { OutputFormat, TransformOptions } from '../types.js';
|
||||
import type { OutputFormat, TransformOptions } from '../loaders/index.js';
|
||||
|
||||
export function isOutputFormat(value: string): value is OutputFormat {
|
||||
return ['avif', 'jpeg', 'png', 'webp'].includes(value);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import sizeOf from 'image-size';
|
||||
import fs from 'node:fs/promises';
|
||||
import { ImageMetadata, InputFormat } from '../types.js';
|
||||
import { InputFormat } from '../loaders/index.js';
|
||||
import { ImageMetadata } from '../vite-plugin-astro-image.js';
|
||||
|
||||
export async function metadata(src: string): Promise<ImageMetadata | undefined> {
|
||||
const file = await fs.readFile(src);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { OUTPUT_DIR } from '../constants.js';
|
||||
import type { TransformOptions } from '../types.js';
|
||||
import type { TransformOptions } from '../loaders/index.js';
|
||||
import { isRemoteImage } from './images.js';
|
||||
import { shorthash } from './shorthash.js';
|
||||
|
||||
|
|
|
@ -3,9 +3,17 @@ import { pathToFileURL } from 'node:url';
|
|||
import type { PluginContext } from 'rollup';
|
||||
import slash from 'slash';
|
||||
import type { Plugin, ResolvedConfig } from 'vite';
|
||||
import type { IntegrationOptions } from './types.js';
|
||||
import type { IntegrationOptions } from './index.js';
|
||||
import type { InputFormat } from './loaders/index.js';
|
||||
import { metadata } from './utils/metadata.js';
|
||||
|
||||
export interface ImageMetadata {
|
||||
src: string;
|
||||
width: number;
|
||||
height: number;
|
||||
format: InputFormat;
|
||||
}
|
||||
|
||||
export function createPlugin(config: AstroConfig, options: Required<IntegrationOptions>): Plugin {
|
||||
const filter = (id: string) =>
|
||||
/^(?!\/_image?).*.(heic|heif|avif|jpeg|jpg|png|tiff|webp|gif)$/.test(id);
|
||||
|
|
Loading…
Reference in a new issue