From 93128881b4998a639b9a72c92059ce540fea80ff Mon Sep 17 00:00:00 2001 From: mricoul Date: Tue, 5 Nov 2024 16:24:58 +0100 Subject: [PATCH 1/2] perf (lazysizes): remove lazysizes, use native lazy attribute instead --- package.json | 1 - src/js/editor.js | 11 --- src/js/index.js | 2 - src/scss/02-tools/_m-placeholder-media.scss | 5 -- src/scss/04-utilities/_lazyload.scss | 77 --------------------- yarn.lock | 8 --- 6 files changed, 104 deletions(-) delete mode 100644 src/scss/04-utilities/_lazyload.scss diff --git a/package.json b/package.json index d0362d5b..58816f05 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "license": "GPL-2.0", "dependencies": { "@fontsource/poppins": "^5.0.5", - "lazysizes": "^5.3.2", "oneloop.js": "^5.2.1" }, "devDependencies": { diff --git a/src/js/editor.js b/src/js/editor.js index fbed2e49..771566b5 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -2,21 +2,10 @@ /* Customize BFFEditorSettings in inc/Services/Editor.php or with `bff_editor_custom_settings` filter (see readme). */ -import lazySizes from 'lazysizes' -import 'lazysizes/plugins/native-loading/ls.native-loading' -import 'lazysizes/plugins/object-fit/ls.object-fit' import domReady from '@wordpress/dom-ready' import { addFilter } from '@wordpress/hooks' import { unregisterBlockStyle, getBlockVariations, unregisterBlockVariation } from '@wordpress/blocks' -/** - * LazySizes configuration - * https://github.com/aFarkas/lazysizes/#js-api---options - */ -lazySizes.cfg.nativeLoading = { - setLoadingAttribute: false, -} - // Native Gutenberg domReady(() => { // Disable specific block styles diff --git a/src/js/index.js b/src/js/index.js index 7a6f043e..64b490f0 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -1,5 +1,3 @@ -import 'lazysizes' -import 'lazysizes/plugins/print/ls.print' import './classes/ScrollDirection' import './classes/ButtonSeoClick' import './classes/Header' diff --git a/src/scss/02-tools/_m-placeholder-media.scss b/src/scss/02-tools/_m-placeholder-media.scss index 9ac0b2d2..dc71bb17 100644 --- a/src/scss/02-tools/_m-placeholder-media.scss +++ b/src/scss/02-tools/_m-placeholder-media.scss @@ -28,11 +28,6 @@ content: ""; } - .lazyload, - .lazyloading { - height: 0; - } - #{$targets} { position: absolute; top: 0; diff --git a/src/scss/04-utilities/_lazyload.scss b/src/scss/04-utilities/_lazyload.scss deleted file mode 100644 index c2a4753e..00000000 --- a/src/scss/04-utilities/_lazyload.scss +++ /dev/null @@ -1,77 +0,0 @@ -@use "../01-abstract/variables" as *; -@use "sass:math"; - -.lazyload, -.lazyloading { - background: $color-primary; - opacity: 0; -} - -.lazyloaded { - opacity: 1; - transition: opacity .5s; -} - -// css loading for bgset items -// with basicspinner -$loading-dimensions: 50px; - -[data-bgset] { - position: relative; - background-repeat: no-repeat; - background-position: 50% 50%; - background-size: cover; - opacity: 1; - - &::before { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 2; - content: ""; - background: $color-primary; - transition: opacity .5s, z-index .5s ease .5s; - } - - &::after { - position: absolute; - top: calc(50% - #{math.div($loading-dimensions, 2)}); - left: calc(50% - #{math.div($loading-dimensions, 2)}); - z-index: 3; - width: $loading-dimensions; - height: $loading-dimensions; - content: ""; - border: 5px solid $color-light; - border-top-color: transparent; - border-radius: $loading-dimensions; - opacity: 1; - transition: opacity .5s, z-index .5s ease .5s; - transform: translateX(-50%) translateY(-50%); - animation: loading .5s linear infinite; - } - - &.lazyload { - opacity: 1; - transition: opacity .5s; - } - - &.lazyloaded { - &::after, - &::before { - z-index: -1; - opacity: 0; - } - } -} - -@keyframes loading { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } -} diff --git a/yarn.lock b/yarn.lock index a313ba09..4f880bde 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2137,7 +2137,6 @@ __metadata: imagemin-jpegtran: "npm:^7.0.0" imagemin-optipng: "npm:^8.0.0" imagemin-svgo: "npm:^10.0.1" - lazysizes: "npm:^5.3.2" mini-css-extract-plugin: "npm:^1.5.0" oneloop.js: "npm:^5.2.1" postcss: "npm:^8.4.24" @@ -6624,13 +6623,6 @@ __metadata: languageName: node linkType: hard -"lazysizes@npm:^5.3.2": - version: 5.3.2 - resolution: "lazysizes@npm:5.3.2" - checksum: 10/15dfa1cc8bf8c3a4393014d411f161cb1df96ab0888a0776ba4dc34d45c6ba9c5d2b2eca22683c14dfeee552c67173cb9a512d640d3424d9910c0246e15c4edc - languageName: node - linkType: hard - "levn@npm:^0.4.1": version: 0.4.1 resolution: "levn@npm:0.4.1" From 4898ab19dfb195ee1815d3bfc5becad039ea587b Mon Sep 17 00:00:00 2001 From: mricoul Date: Mon, 2 Jun 2025 16:31:59 +0200 Subject: [PATCH 2/2] feat (config): update Webpack image sizes plugin Introduces a new Webpack plugin to automatically generate `image-locations.json` and `image-sizes.json` based on JSON size definitions and TPL templates. This allows for dynamic image size management and srcset generation, improving image optimization and reducing manual configuration. The plugin supports default image generation and format conversion using Sharp. --- assets/conf-img/sizes/square-native.json | 12 + .../conf-img/tpl/default-native-caption.tpl | 5 + assets/conf-img/tpl/default-native.tpl | 2 + config/WebpackImageSizesPlugin.js | 266 -------- config/plugins.js | 12 +- config/webpack-image-sizes-plugin.js | 509 +++++++++++++++ package.json | 2 +- src/scss/editor.scss | 1 - src/scss/style.scss | 1 - yarn.lock | 609 +++++++++--------- 10 files changed, 829 insertions(+), 590 deletions(-) create mode 100644 assets/conf-img/sizes/square-native.json create mode 100644 assets/conf-img/tpl/default-native-caption.tpl create mode 100644 assets/conf-img/tpl/default-native.tpl delete mode 100644 config/WebpackImageSizesPlugin.js create mode 100644 config/webpack-image-sizes-plugin.js diff --git a/assets/conf-img/sizes/square-native.json b/assets/conf-img/sizes/square-native.json new file mode 100644 index 00000000..957b72d7 --- /dev/null +++ b/assets/conf-img/sizes/square-native.json @@ -0,0 +1,12 @@ +[ + { + "width": 500, + "height": 500, + "crop": true + }, + { + "width": 1000, + "height": 1000, + "crop": true + } +] diff --git a/assets/conf-img/tpl/default-native-caption.tpl b/assets/conf-img/tpl/default-native-caption.tpl new file mode 100644 index 00000000..daf9c71e --- /dev/null +++ b/assets/conf-img/tpl/default-native-caption.tpl @@ -0,0 +1,5 @@ +%%data-location%% +
+ +
%%caption%%
+
diff --git a/assets/conf-img/tpl/default-native.tpl b/assets/conf-img/tpl/default-native.tpl new file mode 100644 index 00000000..baf82163 --- /dev/null +++ b/assets/conf-img/tpl/default-native.tpl @@ -0,0 +1,2 @@ +%%data-location%% + diff --git a/config/WebpackImageSizesPlugin.js b/config/WebpackImageSizesPlugin.js deleted file mode 100644 index e718dbd6..00000000 --- a/config/WebpackImageSizesPlugin.js +++ /dev/null @@ -1,266 +0,0 @@ -const chalk = require('chalk') -const path = require('path') -const fs = require('fs') -const sharp = require('sharp') - -const logId = '[' + chalk.blue('WebpackImageSizesPlugin') + ']' - -class WebpackImageSizesPlugin { - constructor(options) { - // folders - this._confImgFolder = path.resolve(__dirname, '../assets/conf-img') + '/' - this._tplFolder = this._confImgFolder + 'tpl/' - this._defaultImagesFolder = path.resolve(__dirname, '../dist/images') + '/' - - // files - this._imageSisesJson = this._confImgFolder + 'image-sizes.json' - this._imageLocationsJson = this._confImgFolder + 'image-locations.json' - this._imageDefault = path.resolve(__dirname, '../src/img/static') + '/default.jpg' - - // list of [filnename]: sizes - this._defaultImages = {} - - if (options.watch) { - fs.watch(this._tplFolder, () => { - this.generateImageJsonFiles() - this.generateDefaultImages() - }) - } - - this.generateImageJsonFiles() - } - - /** - * apply - */ - apply(compiler) { - compiler.hooks.afterEmit.tapAsync('WebpackImageSizesPlugin', (compilation, callback) => { - this.generateDefaultImages() - callback() - }) - } - - /** - * Generate image-sises.json and image-location.json - */ - generateImageJsonFiles() { - const that = this - const regex = { - srcset: /srcset="(.[^"]*)"/gm, - crop: /data-crop="(.[^"]*)"/gm, - img: /img-\d*-\d*/gm, - } - - const locations = {} - const sizes = {} - - let nbLocations = 0 - let nbSizes = 0 - - /** - * reset defaultImages - */ - this._defaultImages = {} - - /** - * Return an array of names of tpl files - * @return {array} - */ - function getTemplateFileNames() { - return fs.readdirSync(that._tplFolder).filter((tpl) => { - if ( - tpl !== 'default-picture.tpl' && - tpl !== 'default-picture-caption.tpl' && - tpl !== 'default-picture-nolazyload.tpl' && - tpl !== 'default-picture-nolazyload-caption.tpl' - ) { - return tpl - } - }) - } - - /** - * Return content of tpl file - * @param {string} templateFileName - * @return {string} - */ - function getTemplateFileContent(templateFileName) { - return fs.readFileSync(that._tplFolder + templateFileName, 'utf8') - } - - /** - * Create a json file - * @param {string} destPath - * @param data - * @return undefined - */ - function createJsonFile(destPath, data) { - createFile(destPath, JSON.stringify(data, null, 2)) - } - - /** - * Remove extension template name - * @param {string} name - * @return {String} - */ - function removeFileExtension(name) { - return name.split('.')[0] - } - - /** - * Generate default location name based on image size - * @param {String} size - * @return {String} - */ - function getDefaultImgName(str) { - return `${str.replace('img', 'default')}.jpg` - } - - /** - * Check if srcset is retina - * @param {String} src - * @return {Array} - */ - function isRetina(src) { - const retina = [] - src.split(',').forEach((val) => { - if (val.includes('2x')) { - retina.push('2x') - } else { - retina.push('') - } - }) - return retina - } - - /** - * Create file if he does not exist - * @param {String} filename - * @param {String} json - */ - function createFile(filename, json) { - fs.open(filename, 'r', () => { - fs.writeFileSync(filename, json) - }) - } - - /** - * Get image locations informations from tpl files - */ - function imageLocationsFromTpl() { - const templateFileNames = getTemplateFileNames() - - templateFileNames.forEach((tplName) => { - nbLocations += 1 - const tplContent = getTemplateFileContent(tplName) - const srcsetArr = tplContent.match(regex.srcset) || [] - const cropArr = tplContent.match(regex.crop) - const storage = { - srcsets: [], - default_img: '', - img_base: '', - } - - srcsetArr.forEach((src) => { - const dimensions = src.match(regex.img) - const retina = isRetina(src) - const crop = !(cropArr && cropArr[0] === 'data-crop="false"') - - dimensions.forEach((size, index) => { - const splitSize = size.split('-') - const srcsetObj = { - srcset: retina[index], - size, - } - - if (sizes[size] && sizes[size].crop !== crop) { - console.log(logId, '\nSize already exists but crop is not equal :', size) - } - - if (!sizes[size]) { - sizes[size] = { - width: splitSize[1], - height: splitSize[2], - crop, - } - - nbSizes += 1 - } - - storage.srcsets.push(srcsetObj) - storage.default_img = getDefaultImgName(size) - storage.img_base = size - - that._defaultImages[getDefaultImgName(size)] = sizes[size] - }) - - locations[removeFileExtension(tplName)] = [storage] - }) - }) - } - - /** - * Init - */ - console.log(logId, 'Generate image locations and sizes JSON files') - - imageLocationsFromTpl() - - createJsonFile(this._imageLocationsJson, [locations]) - createJsonFile(this._imageSisesJson, [sizes]) - - console.log(logId, 'JSON files successfully generated !') - console.log(logId, 'Number of locations:', nbLocations) - console.log(logId, 'Number of sizes:', nbSizes) - - return this - } - - /** - * generate default images - */ - generateDefaultImages() { - if (!fs.existsSync(this._defaultImagesFolder)) { - fs.mkdirSync(this._defaultImagesFolder, { recursive: true }) - } - - for (const filename in this._defaultImages) { - const sizes = this._defaultImages[filename] - const outputFile = this._defaultImagesFolder + filename - - try { - if (fs.existsSync(outputFile)) { - continue - } - - const width = parseInt(sizes.width, 10) - const height = parseInt(sizes.height, 10) - - if (width >= 9999 || height >= 9999) { - continue - } - - sharp(this._imageDefault) - .resize(width, height, { - fit: 'cover', - position: 'center', - }) - .jpeg({ quality: 70, chromaSubsampling: '4:4:4' }) - .toFile(outputFile, (err, info) => { - if (err) { - console.error(logId, err) - } else { - console.log(logId, `Created ${info.width}x${info.height} image`) - console.log(logId, 'at', outputFile.split('/themes/')[1] || '') - } - }) - } catch (err) { - console.error(logId, err) - } - } - - return this - } -} - -module.exports = WebpackImageSizesPlugin diff --git a/config/plugins.js b/config/plugins.js index be971bb1..866ecb12 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -9,7 +9,7 @@ const WebpackBar = require('webpackbar') const DependencyExtractionWebpackPlugin = require('@wordpress/dependency-extraction-webpack-plugin') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin -const WebpackImageSizesPlugin = require('./WebpackImageSizesPlugin') +const WebpackImageSizesPlugin = require('./webpack-image-sizes-plugin') module.exports = { get: function (mode) { @@ -33,7 +33,15 @@ module.exports = { }), new DependencyExtractionWebpackPlugin(), new WebpackImageSizesPlugin({ - watch: mode !== 'production', + confImgPath: 'assets/conf-img', // Path to the conf-img folder + sizesSubdir: 'sizes', // Subdirectory containing the sizes JSON files + tplSubdir: 'tpl', // Subdirectory containing TPL templates + outputImageLocations: 'image-locations.json', // Output locations file name + outputImageSizes: 'image-sizes.json', // Output sizes file name + generateDefaultImages: true, // Generate default images + defaultImageSource: 'src/img/static/default.jpg', // Source image for generation + defaultImagesOutputDir: 'dist/images', // Default images output directory + defaultImageFormat: 'jpg', // Generated image format (jpg, png, webp, avif) }), ] diff --git a/config/webpack-image-sizes-plugin.js b/config/webpack-image-sizes-plugin.js new file mode 100644 index 00000000..f8cac011 --- /dev/null +++ b/config/webpack-image-sizes-plugin.js @@ -0,0 +1,509 @@ +const fs = require('fs') +const path = require('path') + +// Try to require sharp, fallback gracefully if not available +let sharp +try { + sharp = require('sharp') +} catch (error) { + console.warn('⚠️ Sharp not available. Default image generation will be disabled.') +} + +/** + * Webpack plugin that generates image-locations.json and image-sizes.json files + * by parsing JSON files in sizes/ subdirectory and TPL files in tpl/ subdirectory. + * + * @class WebpackImageSizesPlugin + */ +class WebpackImageSizesPlugin { + /** + * Creates an instance of WebpackImageSizesPlugin. + * + * @param {Object} [options={}] - Plugin configuration options + * @param {string} [options.confImgPath='assets/conf-img'] - Path to the conf-img directory + * @param {string} [options.sizesSubdir='sizes'] - Subdirectory containing JSON size files + * @param {string} [options.tplSubdir='tpl'] - Subdirectory containing TPL template files + * @param {string} [options.outputImageLocations='image-locations.json'] - Output filename for image locations + * @param {string} [options.outputImageSizes='image-sizes.json'] - Output filename for image sizes + * @param {boolean} [options.generateDefaultImages=false] - Whether to generate default images + * @param {string} [options.defaultImageSource='src/img/static/default.jpg'] - Path to the source image for generating defaults + * @param {string} [options.defaultImagesOutputDir='dist/images'] - Directory where default images will be generated + * @param {string} [options.defaultImageFormat='jpg'] - Format for generated images (jpg, png, webp, avif) + * @memberof WebpackImageSizesPlugin + */ + constructor(options = {}) { + this.options = { + confImgPath: options.confImgPath || 'assets/conf-img', + sizesSubdir: options.sizesSubdir || 'sizes', + tplSubdir: options.tplSubdir || 'tpl', + outputImageLocations: options.outputImageLocations || 'image-locations.json', + outputImageSizes: options.outputImageSizes || 'image-sizes.json', + generateDefaultImages: options.generateDefaultImages || false, + defaultImageSource: options.defaultImageSource || 'src/img/static/default.jpg', + defaultImagesOutputDir: options.defaultImagesOutputDir || 'dist/images', + defaultImageFormat: options.defaultImageFormat || 'jpg', + ...options, + } + } + + /** + * Applies the plugin to the webpack compiler. + * Sets up hooks for initial build and watch mode rebuilds. + * + * @param {Object} compiler - The webpack compiler instance + * @memberof WebpackImageSizesPlugin + */ + apply(compiler) { + // Execute on first build and each rebuild + const runGeneration = async (compilation, callback) => { + try { + const confImgPath = path.resolve(compiler.context, this.options.confImgPath) + const sizesPath = path.join(confImgPath, this.options.sizesSubdir) + const tplPath = path.join(confImgPath, this.options.tplSubdir) + + console.log('🔧 Starting WebpackImageSizesPlugin generation...') + + // Check for deleted/renamed files if output files already exist + this.checkForDeletedFiles(confImgPath, sizesPath, tplPath) + + // Generate image-locations.json from JSON and TPL files + const imageLocations = this.generateImageLocations(sizesPath, tplPath) + + // Generate image-sizes.json from JSON files and TPL files + const imageSizes = this.generateImageSizes(sizesPath, tplPath, imageLocations) + + // Write output files + const imageLocationsPath = path.join(confImgPath, this.options.outputImageLocations) + const imageSizesPath = path.join(confImgPath, this.options.outputImageSizes) + + fs.writeFileSync(imageLocationsPath, JSON.stringify(imageLocations, null, 2)) + fs.writeFileSync(imageSizesPath, JSON.stringify(imageSizes, null, 2)) + + console.log(`✅ Generated ${this.options.outputImageLocations} and ${this.options.outputImageSizes}`) + + // Generate default images if option is enabled + if (this.options.generateDefaultImages) { + await this.generateDefaultImages(compiler.context, imageSizes) + } + + if (callback) { + callback() + } + } catch (error) { + console.error('❌ Error in WebpackImageSizesPlugin:', error) + if (callback) { + callback(error) + } + } + } + + // Hook for initial build + compiler.hooks.emit.tapAsync('WebpackImageSizesPlugin', runGeneration) + + // Hook for rebuilds in watch mode + compiler.hooks.watchRun.tapAsync('WebpackImageSizesPlugin', (compiler, callback) => { + console.log('👀 Watch mode: checking for conf-img changes...') + runGeneration(null, callback) + }) + + // Add directories to watch + compiler.hooks.afterEnvironment.tap('WebpackImageSizesPlugin', () => { + const confImgPath = path.resolve(compiler.context, this.options.confImgPath) + const sizesPath = path.join(confImgPath, this.options.sizesSubdir) + const tplPath = path.join(confImgPath, this.options.tplSubdir) + + // Add directories to watched dependencies + compiler.hooks.compilation.tap('WebpackImageSizesPlugin', (compilation) => { + // Watch configuration directories + if (fs.existsSync(sizesPath)) { + compilation.contextDependencies.add(sizesPath) + console.log('📁 Added sizes directory to watch dependencies') + } + if (fs.existsSync(tplPath)) { + compilation.contextDependencies.add(tplPath) + console.log('📁 Added tpl directory to watch dependencies') + } + }) + }) + } + + /** + * Generates image sizes configuration by parsing JSON files in the sizes directory + * and extracting sizes from TPL files. + * + * @param {string} sizesPath - Path to the sizes directory containing JSON files + * @param {string} tplPath - Path to the tpl directory containing template files + * @param {Array} imageLocations - Generated image locations to extract additional sizes + * @returns {Array} Array containing image sizes configuration object + * @memberof WebpackImageSizesPlugin + */ + generateImageSizes(sizesPath, tplPath, imageLocations) { + // Completely reset image sizes + const imageSizes = [{}] + const allSizes = new Set() // To avoid duplicates + + if (!fs.existsSync(sizesPath)) { + console.warn(`⚠️ Sizes directory not found: ${sizesPath}`) + return imageSizes + } + + const sizeFiles = fs.readdirSync(sizesPath).filter((file) => file.endsWith('.json')) + console.log(`📋 Processing ${sizeFiles.length} size files: ${sizeFiles.join(', ')}`) + + sizeFiles.forEach((file) => { + try { + const filePath = path.join(sizesPath, file) + const sizesData = JSON.parse(fs.readFileSync(filePath, 'utf8')) + + // Convert sizes to image-sizes.json format + sizesData.forEach((size) => { + const sizeKey = `img-${size.width}-${size.height}` + + // Avoid duplicates between files + if (!allSizes.has(sizeKey)) { + allSizes.add(sizeKey) + imageSizes[0][sizeKey] = { + width: size.width.toString(), + height: size.height.toString(), + crop: size.crop, + } + } + }) + } catch (error) { + console.error(`❌ Error parsing ${file}:`, error) + } + }) + + // Extract additional sizes from TPL files that are not in JSON files + this.extractSizesFromTPLFiles(tplPath, imageLocations, imageSizes[0], allSizes) + + console.log(`📐 Generated ${Object.keys(imageSizes[0]).length} unique image sizes`) + return imageSizes + } + + /** + * Extracts image sizes from TPL files and adds them to the sizes configuration. + * This method parses image locations to find sizes that are referenced in TPL files + * but not defined in JSON files. + * + * @param {string} tplPath - Path to the tpl directory + * @param {Array} imageLocations - Generated image locations containing srcsets + * @param {Object} imageSizesObj - Image sizes object to populate + * @param {Set} allSizes - Set of already processed sizes to avoid duplicates + * @memberof WebpackImageSizesPlugin + */ + extractSizesFromTPLFiles(tplPath, imageLocations, imageSizesObj, allSizes) { + if (!fs.existsSync(tplPath) || !imageLocations[0]) { + return + } + + // Extract all unique sizes from image locations + const tplSizes = new Set() + + Object.values(imageLocations[0]).forEach((locationArray) => { + locationArray.forEach((location) => { + if (location.srcsets) { + location.srcsets.forEach((srcset) => { + if (srcset.size && srcset.size.startsWith('img-')) { + tplSizes.add(srcset.size) + } + }) + } + }) + }) + + // Add sizes that are not already defined + tplSizes.forEach((sizeKey) => { + if (!allSizes.has(sizeKey)) { + // Extract width and height from size key (e.g., "img-100-100" -> width: 100, height: 100) + const matches = sizeKey.match(/img-(\d+)-(\d+)/) + if (matches) { + const width = matches[1] + const height = matches[2] + + allSizes.add(sizeKey) + imageSizesObj[sizeKey] = { + width: width, + height: height, + crop: true, // Default crop value for TPL-extracted sizes + } + + console.log(`🎨 Added size from TPL: ${sizeKey}`) + } + } + }) + } + + /** + * Generates image locations configuration by parsing JSON and TPL files. + * + * @param {string} sizesPath - Path to the sizes directory containing JSON files + * @param {string} tplPath - Path to the tpl directory containing template files + * @returns {Array} Array containing image locations configuration object + * @memberof WebpackImageSizesPlugin + */ + generateImageLocations(sizesPath, tplPath) { + // Completely reset image locations + const imageLocations = [{}] + const processedFiles = new Set() // For tracking processed files + + if (!fs.existsSync(sizesPath)) { + console.warn(`⚠️ Sizes directory not found: ${sizesPath}`) + return imageLocations + } + + // Process JSON files in sizes/ first + const sizeFiles = fs.readdirSync(sizesPath).filter((file) => file.endsWith('.json')) + console.log(`📋 Processing ${sizeFiles.length} JSON files from sizes/: ${sizeFiles.join(', ')}`) + + sizeFiles.forEach((file) => { + try { + const filename = path.basename(file, '.json') + const filePath = path.join(sizesPath, file) + const sizesData = JSON.parse(fs.readFileSync(filePath, 'utf8')) + + // Generate srcsets from sizes + const srcsets = sizesData.map((size) => ({ + size: `img-${size.width}-${size.height}`, + })) + + imageLocations[0][filename] = [ + { + srcsets: srcsets, + }, + ] + + processedFiles.add(filename) + } catch (error) { + console.error(`❌ Error parsing JSON ${file}:`, error) + } + }) + + // Then process TPL files for unprocessed or missing files + if (fs.existsSync(tplPath)) { + const tplFiles = fs.readdirSync(tplPath).filter((file) => file.endsWith('.tpl')) + console.log(`📋 Processing ${tplFiles.length} TPL files: ${tplFiles.join(', ')}`) + + tplFiles.forEach((file) => { + try { + const filename = path.basename(file, '.tpl') + const filePath = path.join(tplPath, file) + const tplContent = fs.readFileSync(filePath, 'utf8') + + // Extract sizes from templates (pattern %%img-width-height%%) + const sizeMatches = tplContent.match(/%%img-(\d+)-(\d+)%%/g) + + // Process only if not already processed by a JSON file or no JSON match + if (sizeMatches && !processedFiles.has(filename)) { + const srcsets = [...new Set(sizeMatches)].map((match) => { + const size = match.replace(/%%/g, '') + return { size } + }) + + imageLocations[0][filename] = [ + { + srcsets: srcsets, + }, + ] + + processedFiles.add(filename) + console.log(`📝 Added location from TPL: ${filename}`) + } + } catch (error) { + console.error(`❌ Error parsing TPL ${file}:`, error) + } + }) + } + + const totalEntries = Object.keys(imageLocations[0]).length + console.log(`📍 Generated ${totalEntries} image locations (${processedFiles.size} files processed)`) + + // Log processed files for debugging + if (processedFiles.size > 0) { + console.log(`✅ Processed files: ${Array.from(processedFiles).join(', ')}`) + } + + return imageLocations + } + + /** + * Generates default images from the source image based on image sizes configuration. + * + * @param {string} compilerContext - The webpack compiler context path + * @param {Array} imageSizes - Array containing image sizes configuration + * @memberof WebpackImageSizesPlugin + */ + async generateDefaultImages(compilerContext, imageSizes) { + if (!sharp) { + console.warn('⚠️ Sharp not available. Skipping default image generation.') + return + } + + const sourceImagePath = path.resolve(compilerContext, this.options.defaultImageSource) + const outputDir = path.resolve(compilerContext, this.options.defaultImagesOutputDir) + + if (!fs.existsSync(sourceImagePath)) { + console.warn(`⚠️ Source image not found: ${sourceImagePath}`) + return + } + + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }) + } + + const sizesObj = imageSizes[0] || {} + const sizeKeys = Object.keys(sizesObj) + const format = this.options.defaultImageFormat.toLowerCase() + + // Validate format + const supportedFormats = ['jpg', 'jpeg', 'png', 'webp', 'avif'] + if (!supportedFormats.includes(format)) { + console.warn(`⚠️ Unsupported format '${format}'. Using 'jpg' instead.`) + this.options.defaultImageFormat = 'jpg' + } + + console.log( + `🖼️ Generating ${sizeKeys.length} default images (${format.toUpperCase()}) from ${ + this.options.defaultImageSource + }` + ) + + const promises = sizeKeys.map(async (sizeKey) => { + const size = sizesObj[sizeKey] + const width = parseInt(size.width) + const height = parseInt(size.height) + const outputFilename = `default-${width}-${height}.${this.options.defaultImageFormat}` + const outputPath = path.join(outputDir, outputFilename) + + try { + let sharpInstance = sharp(sourceImagePath) + + if (size.crop) { + // Crop and resize to exact dimensions + sharpInstance = sharpInstance.resize(width, height, { + fit: 'cover', + position: 'center', + }) + } else { + // Resize maintaining aspect ratio + sharpInstance = sharpInstance.resize(width, height, { + fit: 'inside', + withoutEnlargement: false, + }) + } + + // Apply format-specific options + const formatOptions = this.getFormatOptions(this.options.defaultImageFormat) + await sharpInstance[formatOptions.method](formatOptions.options).toFile(outputPath) + + console.log(`✨ Generated: ${outputFilename} (${width}x${height}${size.crop ? ', cropped' : ''})`) + } catch (error) { + console.error(`❌ Error generating ${outputFilename}:`, error.message) + } + }) + + await Promise.all(promises) + console.log(`🎉 Default image generation completed!`) + } + + /** + * Gets format-specific Sharp options for different image formats. + * + * @param {string} format - The image format (jpg, png, webp, avif) + * @returns {Object} Object containing Sharp method and options + * @memberof WebpackImageSizesPlugin + */ + getFormatOptions(format) { + const formatLower = format.toLowerCase() + + const formatConfigs = { + jpg: { + method: 'jpeg', + options: { quality: 80, progressive: true }, + }, + jpeg: { + method: 'jpeg', + options: { quality: 80, progressive: true }, + }, + png: { + method: 'png', + options: { quality: 80, progressive: true }, + }, + webp: { + method: 'webp', + options: { quality: 80, effort: 4 }, + }, + avif: { + method: 'avif', + options: { quality: 80, effort: 4 }, + }, + } + + return formatConfigs[formatLower] || formatConfigs.jpg + } + + /** + * Checks for deleted or renamed files by comparing current files with existing configuration. + * + * @param {string} confImgPath - Path to the conf-img directory + * @param {string} sizesPath - Path to the sizes directory + * @param {string} tplPath - Path to the tpl directory + * @memberof WebpackImageSizesPlugin + */ + checkForDeletedFiles(confImgPath, sizesPath, tplPath) { + const imageLocationsPath = path.join(confImgPath, this.options.outputImageLocations) + + // Check if image-locations.json file already exists + if (!fs.existsSync(imageLocationsPath)) { + console.log('📄 No existing image-locations.json found, creating fresh files') + return + } + + try { + // Read existing file + const existingData = JSON.parse(fs.readFileSync(imageLocationsPath, 'utf8')) + const existingEntries = Object.keys(existingData[0] || {}) + + // Get current files + const currentSizeFiles = fs.existsSync(sizesPath) + ? fs + .readdirSync(sizesPath) + .filter((file) => file.endsWith('.json')) + .map((file) => path.basename(file, '.json')) + : [] + + const currentTplFiles = fs.existsSync(tplPath) + ? fs + .readdirSync(tplPath) + .filter((file) => file.endsWith('.tpl')) + .map((file) => path.basename(file, '.tpl')) + : [] + + const currentFiles = [...new Set([...currentSizeFiles, ...currentTplFiles])] + + // Detect deleted files + const deletedFiles = existingEntries.filter((entry) => !currentFiles.includes(entry)) + + if (deletedFiles.length > 0) { + console.log(`🗑️ Detected ${deletedFiles.length} deleted/renamed files: ${deletedFiles.join(', ')}`) + console.log(' → These entries will be removed from generated files') + } + + // Detect new files + const newFiles = currentFiles.filter((file) => !existingEntries.includes(file)) + + if (newFiles.length > 0) { + console.log(`📂 Detected ${newFiles.length} new files: ${newFiles.join(', ')}`) + console.log(' → These entries will be added to generated files') + } + + if (deletedFiles.length === 0 && newFiles.length === 0) { + console.log('📋 No file changes detected') + } + } catch (error) { + console.warn('⚠️ Could not read existing image-locations.json:', error.message) + } + } +} + +module.exports = WebpackImageSizesPlugin diff --git a/package.json b/package.json index 58816f05..847a0128 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "prettier": "^2.2.1", "sass": "^1.85.1", "sass-loader": "^16.0.5", - "sharp": "^0.32.1", + "sharp": "^0.34.2", "style-loader": "^2.0.0", "stylelint": "^14.13.0", "stylelint-config-recess-order": "^3.0.0", diff --git a/src/scss/editor.scss b/src/scss/editor.scss index 6e86ec30..25207cf4 100644 --- a/src/scss/editor.scss +++ b/src/scss/editor.scss @@ -27,7 +27,6 @@ variables.$entry-file-name: "editor"; @use "04-utilities/wp-admin-bar"; @use "04-utilities/focus"; -@use "04-utilities/lazyload"; @use "04-utilities/seo"; @use "04-utilities/video-wrapper"; @use "04-utilities/palette"; diff --git a/src/scss/style.scss b/src/scss/style.scss index 4ee6e41e..75d19422 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -31,7 +31,6 @@ variables.$entry-file-name: "style"; @use "04-utilities/wp-admin-bar"; @use "04-utilities/focus"; -@use "04-utilities/lazyload"; @use "04-utilities/seo"; @use "04-utilities/video-wrapper"; @use "04-utilities/palette"; diff --git a/yarn.lock b/yarn.lock index 4f880bde..60bcc8c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -409,6 +409,15 @@ __metadata: languageName: node linkType: hard +"@emnapi/runtime@npm:^1.4.3": + version: 1.4.3 + resolution: "@emnapi/runtime@npm:1.4.3" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/4f90852a1a5912982cc4e176b6420556971bcf6a85ee23e379e2455066d616219751367dcf43e6a6eaf41ea7e95ba9dc830665a52b5d979dfe074237d19578f8 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-arm64@npm:0.16.17" @@ -662,6 +671,195 @@ __metadata: languageName: node linkType: hard +"@img/sharp-darwin-arm64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-darwin-arm64@npm:0.34.2" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-darwin-x64@npm:0.34.2" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.1.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.1.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.1.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-linux-arm@npm:1.1.0" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-ppc64@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-linux-ppc64@npm:1.1.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.1.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-linux-x64@npm:1.1.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.1.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.1.0": + version: 1.1.0 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.1.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-linux-arm64@npm:0.34.2" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-linux-arm@npm:0.34.2" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-linux-s390x@npm:0.34.2" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-linux-x64@npm:0.34.2" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.2" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-linuxmusl-x64@npm:0.34.2" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.1.0" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-wasm32@npm:0.34.2" + dependencies: + "@emnapi/runtime": "npm:^1.4.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-arm64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-win32-arm64@npm:0.34.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-win32-ia32@npm:0.34.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.34.2": + version: 0.34.2 + resolution: "@img/sharp-win32-x64@npm:0.34.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -2003,13 +2201,6 @@ __metadata: languageName: node linkType: hard -"b4a@npm:^1.6.4": - version: 1.6.7 - resolution: "b4a@npm:1.6.7" - checksum: 10/1ac056e3bce378d4d3e570e57319360a9d3125ab6916a1921b95bea33d9ee646698ebc75467561fd6fcc80ff697612124c89bb9b95e80db94c6dc23fcb977705 - languageName: node - linkType: hard - "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -2024,62 +2215,6 @@ __metadata: languageName: node linkType: hard -"bare-events@npm:^2.2.0, bare-events@npm:^2.5.4": - version: 2.5.4 - resolution: "bare-events@npm:2.5.4" - checksum: 10/135ef380b13f554ca2c6905bdbcfac8edae08fce85b7f953fa01f09a9f5b0da6a25e414111659bc9a6118216f0dd1f732016acd11ce91517f2afb26ebeb4b721 - languageName: node - linkType: hard - -"bare-fs@npm:^4.0.1": - version: 4.1.2 - resolution: "bare-fs@npm:4.1.2" - dependencies: - bare-events: "npm:^2.5.4" - bare-path: "npm:^3.0.0" - bare-stream: "npm:^2.6.4" - peerDependencies: - bare-buffer: "*" - peerDependenciesMeta: - bare-buffer: - optional: true - checksum: 10/e64d22d0311a71a23919e33b8f1c2ea9f5a6260c468c9c70bdd248efdf356b4d5a36754d5f502f526ca5ff5544a780afdd73cd66fcc38ba00003e99287a69f37 - languageName: node - linkType: hard - -"bare-os@npm:^3.0.1": - version: 3.6.1 - resolution: "bare-os@npm:3.6.1" - checksum: 10/285d95c391250166128e64da2947f4a348ae127de680afffec1f6c82445856be0d1f259672b471afe06517e4cd3831183c373a1d63ef7799ed4aaa1321b86b67 - languageName: node - linkType: hard - -"bare-path@npm:^3.0.0": - version: 3.0.0 - resolution: "bare-path@npm:3.0.0" - dependencies: - bare-os: "npm:^3.0.1" - checksum: 10/712d90e9cd8c3263cc11b0e0d386d1531a452706d7840c081ee586b34b00d72544e65df7a40013d47c1b177277495225deeede65cb2984db88a979cb65aaa2ff - languageName: node - linkType: hard - -"bare-stream@npm:^2.6.4": - version: 2.6.5 - resolution: "bare-stream@npm:2.6.5" - dependencies: - streamx: "npm:^2.21.0" - peerDependencies: - bare-buffer: "*" - bare-events: "*" - peerDependenciesMeta: - bare-buffer: - optional: true - bare-events: - optional: true - checksum: 10/0f5ca2167fbbccc118157bce7c53a933e21726268e03d751461211550d72b2d01c296b767ccf96aae8ab28e106b126407c6fe0d29f915734b844ffe6057f0a08 - languageName: node - linkType: hard - "base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -2149,7 +2284,7 @@ __metadata: prettier: "npm:^2.2.1" sass: "npm:^1.85.1" sass-loader: "npm:^16.0.5" - sharp: "npm:^0.32.1" + sharp: "npm:^0.34.2" style-loader: "npm:^2.0.0" stylelint: "npm:^14.13.0" stylelint-config-recess-order: "npm:^3.0.0" @@ -2243,17 +2378,6 @@ __metadata: languageName: node linkType: hard -"bl@npm:^4.0.3": - version: 4.1.0 - resolution: "bl@npm:4.1.0" - dependencies: - buffer: "npm:^5.5.0" - inherits: "npm:^2.0.4" - readable-stream: "npm:^3.4.0" - checksum: 10/b7904e66ed0bdfc813c06ea6c3e35eafecb104369dbf5356d0f416af90c1546de3b74e5b63506f0629acf5e16a6f87c3798f16233dcff086e9129383aa02ab55 - languageName: node - linkType: hard - "bluebird@npm:^3.5.0": version: 3.7.2 resolution: "bluebird@npm:3.7.2" @@ -2366,7 +2490,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.2.1, buffer@npm:^5.5.0": +"buffer@npm:^5.2.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -2637,13 +2761,6 @@ __metadata: languageName: node linkType: hard -"chownr@npm:^1.1.1": - version: 1.1.4 - resolution: "chownr@npm:1.1.4" - checksum: 10/115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d - languageName: node - linkType: hard - "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -3372,15 +3489,6 @@ __metadata: languageName: node linkType: hard -"decompress-response@npm:^6.0.0": - version: 6.0.0 - resolution: "decompress-response@npm:6.0.0" - dependencies: - mimic-response: "npm:^3.1.0" - checksum: 10/d377cf47e02d805e283866c3f50d3d21578b779731e8c5072d6ce8c13cc31493db1c2f6784da9d1d5250822120cefa44f1deab112d5981015f2e17444b763812 - languageName: node - linkType: hard - "decompress-tar@npm:^4.0.0, decompress-tar@npm:^4.1.0, decompress-tar@npm:^4.1.1": version: 4.1.1 resolution: "decompress-tar@npm:4.1.1" @@ -3444,13 +3552,6 @@ __metadata: languageName: node linkType: hard -"deep-extend@npm:^0.6.0": - version: 0.6.0 - resolution: "deep-extend@npm:0.6.0" - checksum: 10/7be7e5a8d468d6b10e6a67c3de828f55001b6eb515d014f7aeb9066ce36bd5717161eb47d6a0f7bed8a9083935b465bc163ee2581c8b128d29bf61092fdf57a7 - languageName: node - linkType: hard - "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -3546,10 +3647,10 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.2": - version: 2.0.3 - resolution: "detect-libc@npm:2.0.3" - checksum: 10/b4ea018d623e077bd395f168a9e81db77370dde36a5b01d067f2ad7989924a81d31cb547ff764acb2aa25d50bb7fdde0b0a93bec02212b0cb430621623246d39 +"detect-libc@npm:^2.0.4": + version: 2.0.4 + resolution: "detect-libc@npm:2.0.4" + checksum: 10/136e995f8c5ffbc515955b0175d441b967defd3d5f2268e89fa695e9c7170d8bed17993e31a34b04f0fad33d844a3a598e0fd519a8e9be3cad5f67662d96fee0 languageName: node linkType: hard @@ -3808,7 +3909,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": +"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -4435,13 +4536,6 @@ __metadata: languageName: node linkType: hard -"expand-template@npm:^2.0.3": - version: 2.0.3 - resolution: "expand-template@npm:2.0.3" - checksum: 10/588c19847216421ed92befb521767b7018dc88f88b0576df98cb242f20961425e96a92cbece525ef28cc5becceae5d544ae0f5b9b5e2aa05acb13716ca5b3099 - languageName: node - linkType: hard - "exponential-backoff@npm:^3.1.1": version: 3.1.2 resolution: "exponential-backoff@npm:3.1.2" @@ -4517,13 +4611,6 @@ __metadata: languageName: node linkType: hard -"fast-fifo@npm:^1.2.0, fast-fifo@npm:^1.3.2": - version: 1.3.2 - resolution: "fast-fifo@npm:1.3.2" - checksum: 10/6bfcba3e4df5af7be3332703b69a7898a8ed7020837ec4395bb341bd96cc3a6d86c3f6071dd98da289618cf2234c70d84b2a6f09a33dd6f988b1ff60d8e54275 - languageName: node - linkType: hard - "fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" @@ -5048,13 +5135,6 @@ __metadata: languageName: node linkType: hard -"github-from-package@npm:0.0.0": - version: 0.0.0 - resolution: "github-from-package@npm:0.0.0" - checksum: 10/2a091ba07fbce22205642543b4ea8aaf068397e1433c00ae0f9de36a3607baf5bcc14da97fbb798cfca6393b3c402031fca06d8b491a44206d6efef391c58537 - languageName: node - linkType: hard - "glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -5791,14 +5871,14 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": +"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 languageName: node linkType: hard -"ini@npm:^1.3.4, ini@npm:^1.3.5, ini@npm:~1.3.0": +"ini@npm:^1.3.4, ini@npm:^1.3.5": version: 1.3.8 resolution: "ini@npm:1.3.8" checksum: 10/314ae176e8d4deb3def56106da8002b462221c174ddb7ce0c49ee72c8cd1f9044f7b10cc555a7d8850982c3b9ca96fc212122749f5234bc2b6fb05fb942ed566 @@ -7098,13 +7178,6 @@ __metadata: languageName: node linkType: hard -"mimic-response@npm:^3.1.0": - version: 3.1.0 - resolution: "mimic-response@npm:3.1.0" - checksum: 10/7e719047612411fe071332a7498cf0448bbe43c485c0d780046c76633a771b223ff49bd00267be122cedebb897037fdb527df72335d0d0f74724604ca70b37ad - languageName: node - linkType: hard - "min-indent@npm:^1.0.0": version: 1.0.1 resolution: "min-indent@npm:1.0.1" @@ -7154,7 +7227,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.1.3, minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.6": +"minimist@npm:^1.1.3, minimist@npm:^1.2.0, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f @@ -7254,13 +7327,6 @@ __metadata: languageName: node linkType: hard -"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": - version: 0.5.3 - resolution: "mkdirp-classic@npm:0.5.3" - checksum: 10/3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac - languageName: node - linkType: hard - "mkdirp@npm:^3.0.1": version: 3.0.1 resolution: "mkdirp@npm:3.0.1" @@ -7333,13 +7399,6 @@ __metadata: languageName: node linkType: hard -"napi-build-utils@npm:^2.0.0": - version: 2.0.0 - resolution: "napi-build-utils@npm:2.0.0" - checksum: 10/69adcdb828481737f1ec64440286013f6479d5b264e24d5439ba795f65293d0bb6d962035de07c65fae525ed7d2fcd0baab6891d8e3734ea792fec43918acf83 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -7378,24 +7437,6 @@ __metadata: languageName: node linkType: hard -"node-abi@npm:^3.3.0": - version: 3.74.0 - resolution: "node-abi@npm:3.74.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10/314ba5f773690e12a3d87b967d509e9badf16bf2a8ba7619104794f9594545dd268a42f34817d3c81402bf1dc6308545456e2fa9c0200bb6e648cfb75addbe66 - languageName: node - linkType: hard - -"node-addon-api@npm:^6.1.0": - version: 6.1.0 - resolution: "node-addon-api@npm:6.1.0" - dependencies: - node-gyp: "npm:latest" - checksum: 10/8eea1d4d965930a177a0508695beb0d89b4c1d80bf330646a035357a1e8fc31e0d09686e2374996e96e757b947a7ece319f98ede3146683f162597c0bcb4df90 - languageName: node - linkType: hard - "node-addon-api@npm:^7.0.0": version: 7.1.1 resolution: "node-addon-api@npm:7.1.1" @@ -9008,28 +9049,6 @@ __metadata: languageName: node linkType: hard -"prebuild-install@npm:^7.1.1": - version: 7.1.3 - resolution: "prebuild-install@npm:7.1.3" - dependencies: - detect-libc: "npm:^2.0.0" - expand-template: "npm:^2.0.3" - github-from-package: "npm:0.0.0" - minimist: "npm:^1.2.3" - mkdirp-classic: "npm:^0.5.3" - napi-build-utils: "npm:^2.0.0" - node-abi: "npm:^3.3.0" - pump: "npm:^3.0.0" - rc: "npm:^1.2.7" - simple-get: "npm:^4.0.0" - tar-fs: "npm:^2.0.0" - tunnel-agent: "npm:^0.6.0" - bin: - prebuild-install: bin.js - checksum: 10/1b7e4c00d2750b532a4fc2a83ffb0c5fefa1b6f2ad071896ead15eeadc3255f5babd816949991af083cf7429e375ae8c7d1c51f73658559da36f948a020a3a11 - languageName: node - linkType: hard - "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -9191,20 +9210,6 @@ __metadata: languageName: node linkType: hard -"rc@npm:^1.2.7": - version: 1.2.8 - resolution: "rc@npm:1.2.8" - dependencies: - deep-extend: "npm:^0.6.0" - ini: "npm:~1.3.0" - minimist: "npm:^1.2.0" - strip-json-comments: "npm:~2.0.1" - bin: - rc: ./cli.js - checksum: 10/5c4d72ae7eec44357171585938c85ce066da8ca79146b5635baf3d55d74584c92575fa4e2c9eac03efbed3b46a0b2e7c30634c012b4b4fa40d654353d3c163eb - languageName: node - linkType: hard - "react-dom@npm:^17.0.2": version: 17.0.2 resolution: "react-dom@npm:17.0.2" @@ -9307,7 +9312,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0": +"readable-stream@npm:^3.1.1": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -9880,6 +9885,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.7.2": + version: 7.7.2 + resolution: "semver@npm:7.7.2" + bin: + semver: bin/semver.js + checksum: 10/7a24cffcaa13f53c09ce55e05efe25cd41328730b2308678624f8b9f5fc3093fc4d189f47950f0b811ff8f3c3039c24a2c36717ba7961615c682045bf03e1dda + languageName: node + linkType: hard + "sentence-case@npm:^3.0.4": version: 3.0.4 resolution: "sentence-case@npm:3.0.4" @@ -9972,20 +9986,78 @@ __metadata: languageName: node linkType: hard -"sharp@npm:^0.32.1": - version: 0.32.6 - resolution: "sharp@npm:0.32.6" - dependencies: +"sharp@npm:^0.34.2": + version: 0.34.2 + resolution: "sharp@npm:0.34.2" + dependencies: + "@img/sharp-darwin-arm64": "npm:0.34.2" + "@img/sharp-darwin-x64": "npm:0.34.2" + "@img/sharp-libvips-darwin-arm64": "npm:1.1.0" + "@img/sharp-libvips-darwin-x64": "npm:1.1.0" + "@img/sharp-libvips-linux-arm": "npm:1.1.0" + "@img/sharp-libvips-linux-arm64": "npm:1.1.0" + "@img/sharp-libvips-linux-ppc64": "npm:1.1.0" + "@img/sharp-libvips-linux-s390x": "npm:1.1.0" + "@img/sharp-libvips-linux-x64": "npm:1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.1.0" + "@img/sharp-linux-arm": "npm:0.34.2" + "@img/sharp-linux-arm64": "npm:0.34.2" + "@img/sharp-linux-s390x": "npm:0.34.2" + "@img/sharp-linux-x64": "npm:0.34.2" + "@img/sharp-linuxmusl-arm64": "npm:0.34.2" + "@img/sharp-linuxmusl-x64": "npm:0.34.2" + "@img/sharp-wasm32": "npm:0.34.2" + "@img/sharp-win32-arm64": "npm:0.34.2" + "@img/sharp-win32-ia32": "npm:0.34.2" + "@img/sharp-win32-x64": "npm:0.34.2" color: "npm:^4.2.3" - detect-libc: "npm:^2.0.2" - node-addon-api: "npm:^6.1.0" - node-gyp: "npm:latest" - prebuild-install: "npm:^7.1.1" - semver: "npm:^7.5.4" - simple-get: "npm:^4.0.1" - tar-fs: "npm:^3.0.4" - tunnel-agent: "npm:^0.6.0" - checksum: 10/f0e4a86881e590f86b05ea463229f62cd29afc2dca08b3f597889f872f118c2c456f382bf2c3e90e934b7a1d30f109cf5ed584cf5a23e79d6b6403a8dc0ebe32 + detect-libc: "npm:^2.0.4" + semver: "npm:^7.7.2" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-ppc64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-arm64": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10/8c7a6f20d58849a6e33bc69c4525f471d57eb3dea0072331b55ab12bae4b8bd8b99b65264aeaec38e54d52d692db13e3261f6e7bc29430b39b507c409838cbb0 languageName: node linkType: hard @@ -10101,24 +10173,6 @@ __metadata: languageName: node linkType: hard -"simple-concat@npm:^1.0.0": - version: 1.0.1 - resolution: "simple-concat@npm:1.0.1" - checksum: 10/4d211042cc3d73a718c21ac6c4e7d7a0363e184be6a5ad25c8a1502e49df6d0a0253979e3d50dbdd3f60ef6c6c58d756b5d66ac1e05cda9cacd2e9fc59e3876a - languageName: node - linkType: hard - -"simple-get@npm:^4.0.0, simple-get@npm:^4.0.1": - version: 4.0.1 - resolution: "simple-get@npm:4.0.1" - dependencies: - decompress-response: "npm:^6.0.0" - once: "npm:^1.3.1" - simple-concat: "npm:^1.0.0" - checksum: 10/93f1b32319782f78f2f2234e9ce34891b7ab6b990d19d8afefaa44423f5235ce2676aae42d6743fecac6c8dfff4b808d4c24fe5265be813d04769917a9a44f36 - languageName: node - linkType: hard - "simple-html-tokenizer@npm:^0.5.7": version: 0.5.11 resolution: "simple-html-tokenizer@npm:0.5.11" @@ -10438,20 +10492,6 @@ __metadata: languageName: node linkType: hard -"streamx@npm:^2.15.0, streamx@npm:^2.21.0": - version: 2.22.0 - resolution: "streamx@npm:2.22.0" - dependencies: - bare-events: "npm:^2.2.0" - fast-fifo: "npm:^1.3.2" - text-decoder: "npm:^1.1.0" - dependenciesMeta: - bare-events: - optional: true - checksum: 10/9c329bb316e2085e207e471ecd0da18b4ed5b1cfe5cf10e9e7fad3f8f50c6ca1a6a844bdfd9bc7521560b97f229890de82ca162a0e66115300b91a489b1cbefd - languageName: node - linkType: hard - "strict-uri-encode@npm:^1.0.0": version: 1.1.0 resolution: "strict-uri-encode@npm:1.1.0" @@ -10643,13 +10683,6 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:~2.0.1": - version: 2.0.1 - resolution: "strip-json-comments@npm:2.0.1" - checksum: 10/1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1 - languageName: node - linkType: hard - "strip-outer@npm:^1.0.0": version: 1.0.1 resolution: "strip-outer@npm:1.0.1" @@ -11027,35 +11060,6 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:^2.0.0": - version: 2.1.2 - resolution: "tar-fs@npm:2.1.2" - dependencies: - chownr: "npm:^1.1.1" - mkdirp-classic: "npm:^0.5.2" - pump: "npm:^3.0.0" - tar-stream: "npm:^2.1.4" - checksum: 10/623f7e8e58a43578ba7368002c3cc7e321f6d170053ac0691d95172dbc7daf5dcf4347eb061277627340870ce6cfda89f5a5d633cc274c41ae6d69f54a2374e7 - languageName: node - linkType: hard - -"tar-fs@npm:^3.0.4": - version: 3.0.8 - resolution: "tar-fs@npm:3.0.8" - dependencies: - bare-fs: "npm:^4.0.1" - bare-path: "npm:^3.0.0" - pump: "npm:^3.0.0" - tar-stream: "npm:^3.1.5" - dependenciesMeta: - bare-fs: - optional: true - bare-path: - optional: true - checksum: 10/fdcd1c66dc5e2cad5544ffe7eab9a470b419290b22300c344688df51bf06127963da07a1e3ae23cae80851cd9f60149e80b38e56485dd7a14aea701241ac2f81 - languageName: node - linkType: hard - "tar-stream@npm:^1.5.2": version: 1.6.2 resolution: "tar-stream@npm:1.6.2" @@ -11071,30 +11075,6 @@ __metadata: languageName: node linkType: hard -"tar-stream@npm:^2.1.4": - version: 2.2.0 - resolution: "tar-stream@npm:2.2.0" - dependencies: - bl: "npm:^4.0.3" - end-of-stream: "npm:^1.4.1" - fs-constants: "npm:^1.0.0" - inherits: "npm:^2.0.3" - readable-stream: "npm:^3.1.1" - checksum: 10/1a52a51d240c118cbcd30f7368ea5e5baef1eac3e6b793fb1a41e6cd7319296c79c0264ccc5859f5294aa80f8f00b9239d519e627b9aade80038de6f966fec6a - languageName: node - linkType: hard - -"tar-stream@npm:^3.1.5": - version: 3.1.7 - resolution: "tar-stream@npm:3.1.7" - dependencies: - b4a: "npm:^1.6.4" - fast-fifo: "npm:^1.2.0" - streamx: "npm:^2.15.0" - checksum: 10/b21a82705a72792544697c410451a4846af1f744176feb0ff11a7c3dd0896961552e3def5e1c9a6bbee4f0ae298b8252a1f4c9381e9f991553b9e4847976f05c - languageName: node - linkType: hard - "tar@npm:^7.4.3": version: 7.4.3 resolution: "tar@npm:7.4.3" @@ -11162,15 +11142,6 @@ __metadata: languageName: node linkType: hard -"text-decoder@npm:^1.1.0": - version: 1.2.3 - resolution: "text-decoder@npm:1.2.3" - dependencies: - b4a: "npm:^1.6.4" - checksum: 10/bcdec33c0f070aeac38e46e4cafdcd567a58473ed308bdf75260bfbd8f7dc76acbc0b13226afaec4a169d0cb44cec2ab89c57b6395ccf02e941eaebbe19e124a - languageName: node - linkType: hard - "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -11323,7 +11294,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.3, tslib@npm:^2.1.0": +"tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7