Skip to content

Commit ccb641d

Browse files
authored
feat: allow configuring image optimization libraries config (#144)
* feat: allow configuring image optimization libraries config Allow passing an configuration object to integration factory function to enable users to configure sharp and svgo libraries configuration. BREAKING CHANGE: Removed exported sharpOptions and svgoOptions. Renamed internal image service export from astroImageService to imageService. * style(tests/unit/config.test.ts): add import extension
1 parent abb6564 commit ccb641d

12 files changed

+533
-81
lines changed

.github/workflows/test.yml .github/workflows/e2e.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Test
1+
name: E2E Test
22

33
on:
44
pull_request:
@@ -220,7 +220,7 @@ jobs:
220220
- conversion
221221

222222
# skip if the workflow is called from another workflow
223-
if: ${{ !cancelled() && contains(github.workflow_ref, '/test.yml') }}
223+
if: ${{ !cancelled() && contains(github.workflow_ref, '/e2e.yml') }}
224224

225225
runs-on: ubuntu-22.04
226226

.github/workflows/release.yml

+11-3
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,25 @@ jobs:
1919
actions: read
2020
uses: ./.github/workflows/check.yml
2121

22-
test:
22+
unit:
2323
permissions:
2424
# we must pass all the required permissions of the callee workflow
2525
contents: read
2626
actions: read
27-
uses: ./.github/workflows/test.yml
27+
uses: ./.github/workflows/unit.yml
28+
29+
e2e:
30+
permissions:
31+
# we must pass all the required permissions of the callee workflow
32+
contents: read
33+
actions: read
34+
uses: ./.github/workflows/e2e.yml
2835

2936
release:
3037
needs:
3138
- check
32-
- test
39+
- e2e
40+
- unit
3341

3442
runs-on: ubuntu-22.04
3543

.github/workflows/unit.yml

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Unit Test
2+
3+
on:
4+
pull_request:
5+
workflow_call:
6+
workflow_dispatch:
7+
schedule:
8+
- cron: '0 0 * * SUN'
9+
10+
permissions: {}
11+
12+
defaults:
13+
run:
14+
shell: bash
15+
16+
jobs:
17+
check:
18+
runs-on: ubuntu-22.04
19+
20+
permissions:
21+
contents: read # for checkout
22+
actions: read # for actions-timeline
23+
24+
steps:
25+
- name: actions-timeline
26+
# skip if the workflow is called from another workflow
27+
if: contains(github.workflow_ref, '/unit.yml')
28+
# cspell:ignore kesin
29+
uses: Kesin11/actions-timeline@1c2ab3f28225878ae4dd1f76d31279f16ea29e29 # v2.1.1
30+
31+
- name: Checkout
32+
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
33+
34+
- name: Setup bun
35+
uses: oven-sh/setup-bun@8f24390df009a496891208e5e36b8a1de1f45135 # v1.2.1
36+
37+
- name: Setup Node.js
38+
# Install to use the latest version of Node.js when shebang is specified
39+
# ref: https://bun.sh/docs/cli/run#bun
40+
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
41+
with:
42+
node-version-file: package.json
43+
44+
- name: Install Dependencies
45+
id: install
46+
run: bun install --frozen-lockfile
47+
env:
48+
HUSKY: 0
49+
50+
- name: Test
51+
run: bun run test:unit

README.md

+81-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
- Convert SVG images to raster images using [sharp](https://github.com/lovell/sharp).
1010
- And, of course, all features of Astro's default image service (`sharpImageService`) are supported.
1111

12-
## ️ Installation
12+
## 🛠️ Installation
1313

1414
### Using `astro add` (recommended)
1515

@@ -39,16 +39,17 @@ npm install astro-better-image-service
3939

4040
```javascript
4141
import betterImageService from "astro-better-image-service";
42+
import { defineConfig } from "astro/config";
4243

43-
export default {
44+
export default defineConfig({
4445
// ...
4546
integrations: [
4647
// ... other integrations
4748
betterImageService(),
4849
// ... other integrations
4950
],
5051
// ...
51-
};
52+
});
5253
```
5354

5455
You may put the `betterImageService` integration anywhere in the `integrations` array.
@@ -93,8 +94,9 @@ For example, you may use the following configuration.
9394
```javascript
9495
import betterImageService from "astro-better-image-service";
9596
import astroCompress from "astro-compress";
97+
import { defineConfig } from "astro/config";
9698

97-
export default {
99+
export default defineConfig({
98100
integrations: [
99101
betterImageService(),
100102
astroCompress.default({
@@ -105,7 +107,81 @@ export default {
105107
SVG: false,
106108
}),
107109
],
108-
};
110+
});
111+
```
112+
113+
## ⚙️ Configuration
114+
115+
If you want to configure the configuration of the image compression and conversion, you may pass a configuration object to the `betterImageService` function.
116+
The configuration object is merged with the default configuration object, exported as `defaultConfig` from the package.
117+
118+
`astro.config.{ts,js,mjs,cjs}`
119+
120+
<!-- cspell:ignore webp -->
121+
122+
```javascript
123+
import betterImageService from "astro-better-image-service";
124+
import { defineConfig } from "astro/config";
125+
126+
export default defineConfig({
127+
// ...
128+
integrations: [
129+
betterImageService({
130+
sharp: {
131+
sharp: {
132+
// sharp constructor options
133+
},
134+
png: {
135+
// sharp png options
136+
},
137+
jpeg: {
138+
// sharp jpeg options
139+
},
140+
webp: {
141+
// sharp webp options
142+
},
143+
avif: {
144+
// sharp avif options
145+
},
146+
},
147+
svgo: {
148+
// svgo options
149+
},
150+
}),
151+
],
152+
// ...
153+
});
154+
```
155+
156+
### [`limitInputPixels`](https://docs.astro.build/en/reference/configuration-reference/#imageserviceconfiglimitinputpixels)
157+
158+
You cannot configure `image.service.config.limitInputPixels` in the configuration object unless you set the `image.service.entrypoint` to `sharpImageService`.
159+
We support to set `limitInputPixels` in the configuration object of `betterImageService` for compatibility with the default image service.
160+
However, we recommend setting `sharp.sharp.limitInputPixels` in the configuration object of `betterImageService` for clarity.
161+
For example, you may set `limitInputPixels` to `false` as follows.
162+
163+
`astro.config.{ts,js,mjs,cjs}`
164+
165+
```javascript
166+
import betterImageService from "astro-better-image-service";
167+
import { defineConfig } from "astro/config";
168+
169+
export default defineConfig({
170+
// ...
171+
integrations: [
172+
betterImageService({
173+
// not recommended
174+
limitInputPixels: false,
175+
// recommended
176+
sharp: {
177+
sharp: {
178+
limitInputPixels: false,
179+
}
180+
},
181+
}),
182+
],
183+
// ...
184+
});
109185
```
110186

111187
## 💻 Development

bunfig.toml

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[install]
22
exact = true # enable since we are using Renovate to update dependencies
33
auto = "disable" # to specify the versions in package.json
4+
5+
[test]
6+
coverage = true # enable coverage reports

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131
"check:type:src": "tsc --noEmit --project tsconfig.src.json",
3232
"check:spell": "cspell --cache \"**/*\"",
3333
"check:knip": "knip",
34-
"test": "playwright test vrt format",
35-
"test:snapshots": "playwright test vrt --update-snapshots",
34+
"test:unit": "bun test tests/unit",
35+
"test:e2e": "playwright test vrt format",
36+
"test:e2e:snapshots": "playwright test vrt --update-snapshots",
3637
"ignore-sync": "ignore-sync .",
3738
"prepare": "husky"
3839
},

src/config.ts

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// import from astro instead of astro/config because ImageServiceConfig in astro/config is not generic
2+
import type { ImageServiceConfig } from "astro";
3+
import type { SharpImageServiceConfig } from "astro/config";
4+
import type {
5+
AvifOptions,
6+
JpegOptions,
7+
PngOptions,
8+
SharpOptions,
9+
WebpOptions,
10+
} from "sharp";
11+
import type { Config as SvgoConfig } from "svgo";
12+
13+
interface LibrariesConfig {
14+
/**
15+
* Configuration for sharp.
16+
*/
17+
sharp?: {
18+
/**
19+
* Configuration for sharp constructor.
20+
*/
21+
sharp?: SharpOptions;
22+
png?: PngOptions;
23+
jpeg?: JpegOptions;
24+
webp?: WebpOptions;
25+
avif?: AvifOptions;
26+
};
27+
/**
28+
* Configuration for svgo.
29+
*/
30+
svgo?: SvgoConfig;
31+
}
32+
33+
/**
34+
* Type of the configuration for the image optimization libraries.
35+
*/
36+
export type Config = LibrariesConfig & SharpImageServiceConfig;
37+
38+
/**
39+
* Type of the merged configuration for the image optimization libraries.
40+
*/
41+
export type MergedConfig = Omit<
42+
Required<Config>,
43+
"sharp" | "limitInputPixels"
44+
> & {
45+
sharp: Required<Required<Config>["sharp"]>;
46+
};
47+
48+
/**
49+
* Default configuration for the image optimization libraries.
50+
*/
51+
export const defaultConfig = {
52+
sharp: {
53+
sharp: {
54+
failOnError: false,
55+
pages: -1,
56+
},
57+
png: {
58+
compressionLevel: 9,
59+
palette: true,
60+
effort: 10,
61+
},
62+
jpeg: {
63+
// cspell:ignore mozjpeg
64+
mozjpeg: true,
65+
},
66+
webp: {
67+
effort: 6,
68+
},
69+
avif: {
70+
// cspell:ignore subsampling
71+
chromaSubsampling: "4:2:0",
72+
effort: 9,
73+
},
74+
},
75+
svgo: {
76+
// cspell:ignore multipass
77+
multipass: true,
78+
js2svg: {
79+
indent: 0,
80+
pretty: false,
81+
},
82+
plugins: [
83+
{
84+
name: "preset-default",
85+
params: {
86+
overrides: {
87+
// viewBox is important for some use cases
88+
// ref: https://github.com/svg/svgo/issues/1128
89+
removeViewBox: false,
90+
},
91+
},
92+
},
93+
],
94+
},
95+
} as const satisfies Required<LibrariesConfig>;
96+
97+
/**
98+
* Create an Astro image service configuration that uses the better image service.
99+
* @param config Configuration for the image optimization libraries.
100+
* @returns Astro image service configuration.
101+
*/
102+
export const betterImageService = (
103+
config: Config & SharpImageServiceConfig = {},
104+
): ImageServiceConfig<MergedConfig> => {
105+
if (
106+
"limitInputPixels" in config &&
107+
config.sharp?.sharp &&
108+
"limitInputPixels" in config.sharp.sharp
109+
) {
110+
throw new Error(
111+
"limitInputPixels should not be set in both the root and sharp.sharp configuration. It is recommended to set it in the sharp.sharp configuration.",
112+
);
113+
}
114+
return {
115+
entrypoint: "astro-better-image-service/image-service",
116+
config: {
117+
// shallow merge for sharp configuration
118+
sharp: {
119+
sharp: {
120+
...defaultConfig.sharp.sharp,
121+
...("limitInputPixels" in config
122+
? { limitInputPixels: config.limitInputPixels }
123+
: {}),
124+
...(config.sharp?.sharp ?? {}),
125+
},
126+
png: {
127+
...defaultConfig.sharp.png,
128+
...(config.sharp?.png ?? {}),
129+
},
130+
jpeg: {
131+
...defaultConfig.sharp.jpeg,
132+
...(config.sharp?.jpeg ?? {}),
133+
},
134+
webp: {
135+
...defaultConfig.sharp.webp,
136+
...(config.sharp?.webp ?? {}),
137+
},
138+
avif: {
139+
...defaultConfig.sharp.avif,
140+
...(config.sharp?.avif ?? {}),
141+
},
142+
},
143+
// shallow merge for svgo configuration except for js2svg
144+
svgo: {
145+
...defaultConfig.svgo,
146+
...(config.svgo ?? {}),
147+
js2svg: {
148+
...defaultConfig.svgo.js2svg,
149+
...(config.svgo?.js2svg ?? {}),
150+
},
151+
},
152+
},
153+
};
154+
};

0 commit comments

Comments
 (0)