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

Geoprv/responsive images loader #1011

Merged
merged 71 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
b086297
feat: added loader for responsive images
geopr Aug 31, 2023
ef0106d
chore: improve docs
geopr Sep 5, 2023
822db01
chore: CHANGELOG
geopr Sep 5, 2023
035f5c3
chore: better jsdoc for scale-image-adapter
geopr Sep 6, 2023
283e3ab
fix: default image name in 'src'
geopr Sep 6, 2023
7069974
fix: import of 'path' and 'vm' modules
geopr Sep 6, 2023
82f3d55
feat: support for custom sizes
geopr Sep 6, 2023
a27bfd7
feat: add support for 'defaultSrcPath' and 'formats' options
geopr Sep 6, 2023
7df846d
chore: better jsdoc
geopr Sep 6, 2023
54ca5b6
chore: better docs
geopr Sep 6, 2023
56359b9
chore: typo
geopr Sep 6, 2023
4b3cff4
fix: 'srcset' generating
geopr Sep 6, 2023
029ba3c
chore: better jsdoc
geopr Sep 6, 2023
e1b996f
fix: moved options for the loader to config/default
geopr Sep 6, 2023
5fdbbfd
chore: better docs
geopr Sep 6, 2023
b50c599
chore: docs
geopr Sep 6, 2023
90d11ec
fix: remove defaultSrcPath from base options
geopr Sep 6, 2023
e58aab5
chore: changelog
geopr Sep 8, 2023
a59d6f1
chore: imporove docs
geopr Sep 8, 2023
4b0579a
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Sep 8, 2023
5d091af
chore: typo
geopr Sep 8, 2023
181b378
chore: stylish fixes
kobezzza Sep 13, 2023
4bf36cc
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Sep 19, 2023
ce8b482
feat: move custom loaders to separated folders
geopr Sep 19, 2023
7cb7427
fix: import modules from node:
geopr Sep 19, 2023
97df90d
refactor: rewrote resize method in adapter using async/await
geopr Sep 19, 2023
80b93c3
refactor: simplify resourceQuery parsing
geopr Sep 19, 2023
432b2c1
fix: get rid of possible race when processing images using responsive…
geopr Sep 19, 2023
a58b326
fix: get rid of possible race when processing images using responsive…
geopr Sep 19, 2023
e850ded
chore: add 'json5' dependency
geopr Sep 19, 2023
298e9cb
refactor: `Promise.all` for processing images using responsiveLoader
geopr Sep 19, 2023
1cdc163
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Sep 19, 2023
2ffe9ba
chore: stylish fixes
kobezzza Sep 20, 2023
eb80aa8
Merge branch 'v4' into geoprv/responsive-images-loader
kobezzza Sep 20, 2023
9612c3e
fix: resolving loader aliases
geopr Sep 20, 2023
bbd44f3
fix: remove unsued code
geopr Sep 21, 2023
898a528
fix: processing image using `url-loader` in dev mode
geopr Sep 21, 2023
6638bb9
fix: remove unsued code
geopr Sep 21, 2023
f72da81
fix: webpack public path when compiling loader results
geopr Sep 21, 2023
21f0f1e
feat: added support for `baseSrc` option for v-image directive
geopr Sep 21, 2023
68ec868
chore: better docs
geopr Sep 21, 2023
a9fcb45
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Oct 11, 2023
58fc90a
fix: CHANGELOG
geopr Oct 11, 2023
73e9f3d
fix: CHANGELOD
geopr Oct 11, 2023
7b2071c
feat: add support for themes
geopr Oct 11, 2023
c97eb33
Revert "feat: add support for themes"
geopr Oct 11, 2023
5467ddc
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Oct 11, 2023
5d046f3
fix: CHANGELOG
geopr Oct 11, 2023
2d2399e
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Oct 13, 2023
f6803a5
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Oct 28, 2024
eb7466b
fix: CHANGELOG
geopr Oct 28, 2024
3423c35
fix: image paths
geopr Oct 30, 2024
c5b519f
fix: url loader with responsive-images-loader
kholstinin Nov 1, 2024
e33c3ee
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Nov 3, 2024
9d460a5
fix: image path
geopr Nov 4, 2024
7cf8b56
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Nov 5, 2024
5382575
fix: changelog
geopr Nov 5, 2024
3032920
update yarn.lock
geopr Nov 5, 2024
9c1c607
remove unceseccary changes
geopr Nov 5, 2024
c6c413c
update yarn.lock
geopr Nov 5, 2024
0ce2d6a
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Nov 7, 2024
6748292
fix: typo CHANGELOD -> CHANGELOG
geopr Nov 11, 2024
246e6d3
fix: create `picture` tag instead of `img` in dev mode
geopr Nov 11, 2024
d8e9aa2
:art:
geopr Nov 11, 2024
4ec7a13
fix: throw an error if the resourceQuery was not parsed successfully
geopr Nov 11, 2024
93b3cf4
docs: jsdoc for `this`
geopr Nov 11, 2024
c3d7829
fix: error while parsing resourceQuery
geopr Nov 11, 2024
df1b1e8
refactor: extract details using regexp instead of compiling source code
geopr Nov 11, 2024
d93de86
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Nov 27, 2024
dc1eb39
Merge branch 'v4' into geoprv/responsive-images-loader
geopr Nov 29, 2024
71d8731
:up:
geopr Dec 3, 2024
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ Changelog

_Note: Gaps between patch versions are faulty, broken or test releases._

## v4.0.0-beta.160 (2024-03-12)

#### :rocket: New Feature

* Added a new webpack loader for responsive images `build/webpack/loaders/responsive-images-loader`

## v4.0.0-beta.159 (2024-11-27)

#### :bug: Bug Fix
Expand Down
16 changes: 16 additions & 0 deletions build/webpack/loaders/responsive-images-loader/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Changelog
=========

> **Tags:**
> - :boom: [Breaking Change]
> - :rocket: [New Feature]
> - :bug: [Bug Fix]
> - :memo: [Documentation]
> - :house: [Internal]
> - :nail_care: [Polish]

## v4.0.0-beta.160 (2024-03-12)

#### :rocket: New Feature

* Initial release
161 changes: 161 additions & 0 deletions build/webpack/loaders/responsive-images-loader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# build/webpack/loaders/responsive-images-loader

The loader is used to compress and convert responsive images.
It utilizes the [responsiveLoader](https://github.com/dazuaz/responsive-loader/tree/master)
library underneath for each conversion operation.

### Usage

By default, this loader is configured to be used with the `responsive` query parameter.
This means that you just need to require the desired image and specify this parameter.

```ss
< .my-image v-image = require('path/to/image.png?responsive')
```

Please note that the image you want to apply the loader to should be in the highest quality.
geopr marked this conversation as resolved.
Show resolved Hide resolved

The loader returns the following structure:

```js
({
// 4e3edf6d108c0701 - the image hash
// 346 - 2x size of the original image
// png - the format of the original image
src: '4e3edf6d108c0701-346.png',
sources: [
{
type: 'png',
srcset: {
'1x': 'f6506a0261a44c16-173.png',
'2x': '4e3edf6d108c0701-346.png',
'3x': '19b08609ec6e1165-521.png'
}
},
{
type: 'webp',
srcset: {
'1x': '4e62cb10bc2b3029-173.webp',
'2x': 'f49d341fedd8bdc5-346.webp',
'3x': '4ca48b9469e44566-521.webp'
}
},
{
type: 'avif',
srcset: {
'1x': '71842fd826667798-173.avif',
'2x': '8da0057becea6b31-346.avif',
'3x': 'b6d75fb5bdf3121b-521.avif'
}
}
]
})
```

Please note that this loader is only applied in production mode.
In development mode, the image will be processed using `url-loader`, which will either return the inline image or the path to the image:
geopr marked this conversation as resolved.
Show resolved Hide resolved

```js
require('path/to/small-image.png?responsive'); // {src: 'data:image/png;base64,.....'}
require('path/to/huge-image.png?responsive'); // {src: 'path/to/huge-image.png'}
```

### Options

In addition to the [options](https://github.com/dazuaz/responsive-loader/tree/master#options) provided by the
`responsive-loader`, this loader also offers a few additional ones.

Please note that the options can be specified using only JSON5 notation for a specific image.
For example:

```js
require('image.png?{responsive:true,key1:value1,key2:value2}');
```

The `responsive` field in the above example is the `resourceQuery` of the loader.
This parameter is required and should always be set to `true`
if you want to process your image using this loader.

Alternatively, you can also override the `responsiveImagesOpts` method that specifies
the base options for every image in `config/default`.

The additional options are the following:

#### defaultSrcPath ['2x.[original image extension]']

The path for the default image in the `src` field.
The format should be in the following pattern: `[resolution].[extension]`.

Example of usage:

```js
require('path/to/image.png?{responsive:true,defaultSrcPath:"1x.jpg"}');

/*
{
src: '[hash]-[width].jpg' // Here will be the hashed image scaled by 1x of its original size
}
*/
```

#### formats ['webp', 'avif']

The formats to convert your image to.

#### sizes [1, 2, 3]

Although this option is specified in the `responsive-loader` options,
it works differently with this loader.
The numbers you provide indicate the scaling that should be applied to
the image (1x, 2x, etc.), not the size in pixels.

#### baseSrc [undefined]

If this option is passed, it will be returned in the resulting object only in production.
It's useful if you have your assets on a static server and you want to specify the base path to the image on it:

```ss
< . v-image = require('path/to/image.png?{responsive:true,baseSrc:"path/on/static/server"}')

/*
{
src: '...',
sources: [...],
baseSrc: 'path/on/static/server'
geopr marked this conversation as resolved.
Show resolved Hide resolved
}
*/
```

### Providing multiple custom options

Here is an example of providing multiple options for a specific image:

```js
require('path/to/image.png?{responsive:true,formats:["webp"],sizes:[1,2,3,4],defaultSrcPath:"3x.png"}');

/*
{
src: '41225eda88e198f9-390.png',
sources: [
{
type: 'png',
srcset: {
'1x': '00013521e68e82ad-130.png',
'2x': '1275606184e9d451-260.png',
'3x': '41225eda88e198f9-390.png',
'4x': '19b08609ec6e1165-521.png'
}
},
{
type: 'webp',
srcset: {
'1x': '7bc796c1451c0938-130.webp',
'2x': '65885d25dc384e15-260.webp',
'3x': '1b814b2471ed305d-390.webp',
'4x': '4ca48b9469e44566-521.webp'
}
}
]
}
*/
```
108 changes: 108 additions & 0 deletions build/webpack/loaders/responsive-images-loader/adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/

'use strict';

const sharp = require('sharp');

/**
* Implementation of the 'Adapter' interface
* @see https://github.com/dazuaz/responsive-loader/tree/master#writing-your-own-adapter
*/
class Adapter {
constructor(imagePath) {
this.image = sharp(imagePath);
}

/**
* Returns metadata of the image
*
* @returns {import('@v4fire/storybook-framework-webpack5').Metadata}
* @see https://github.com/dazuaz/responsive-loader/tree/master#writing-your-own-adapter
*/
metadata() {
return this.image.metadata();
}

/**
* Scales the image to the provided amount of its original size and converts to the specified formats
*
* @param {{width: number, mime: string, options: object}} data - information and the image and provided options
* @returns {Promise<{data: Buffer, width: number, height: number}>}
* @see https://github.com/dazuaz/responsive-loader/tree/master#writing-your-own-adapter
*/
async resize({mime, width: scaleBy, options: {quality, sizes}}) {
const
imageClone = this.image.clone(),
{width, height} = await imageClone.metadata();

if (width == null || height == null) {
throw new Error('Unable to receive width and height of the image', this.image);
}

const
scaledImage = this.scale(imageClone, width, height, scaleBy, sizes.length),
convertedImage = this.convert(scaledImage, mime, quality),
{data, info} = await convertedImage.toBuffer({resolveWithObject: true});

return {data, width: info.width, height: info.height};
}

/**
* Scales the image by the provided amount of its original size
*
* @param {import('sharp').Sharp} image
* @param {number} width
* @param {number} height
* @param {number} scaleBy
* @param {number} maxScaleSize
* @returns {import('sharp').Sharp}
*/
scale(image, width, height, scaleBy, maxScaleSize) {
if (scaleBy === maxScaleSize) {
return image;
}

const
stepWidth = Math.floor(width / maxScaleSize),
stepHeight = Math.floor(height / maxScaleSize);

return image.resize(
stepWidth * scaleBy,
stepHeight * scaleBy
);
}

/**
* Converts the original image to another format
*
* @param {import('sharp').Sharp} image
* @param {string} mimeType
* @param {number} quality
* @returns {import('sharp').Sharp}
*/
convert(image, mimeType, quality) {
const formatMethods = {
'image/png': image.png,
'image/jpeg': image.jpeg,
'image/jpg': image.jpeg,
'image/webp': image.webp,
'image/avif': image.avif
};

return formatMethods[mimeType]?.call(image, {quality});
}
}

/**
* Adapter for scaling an image by the provided amount of its original size
*
* @param {string} imagePath
* @returns {Adapter}
*/
module.exports = (imagePath) => new Adapter(imagePath);
Loading
Loading