UNPKG

@vitbokisch/next-optimized-images

Version:

Automatically optimize images used in next.js projects (jpeg, png, gif, svg).

232 lines (210 loc) 5.74 kB
import { createRequire } from 'module' import { applyImgLoader } from './img-loader.js' import { applyWebpLoader } from './webp-loader.js' import { applyResponsiveLoader } from './responsive-loader.js' import { applyFileLoader } from './file-loader.js' const require = createRequire(import.meta.url) /** * Checks if a node module is installed in the current context * * @param {string} name - module name * @param {string} resolvePath - optional resolve path * @returns {boolean} */ const isModuleInstalled = (name, resolvePath) => { try { require.resolve(name, resolvePath ? { paths: [resolvePath] } : undefined) return true } catch (e) { return false } } /** * Detects all currently installed image optimization loaders * * @param {string} resolvePath - optional resolve path * @returns {object} */ const detectLoaders = (resolvePath) => { const jpeg = isModuleInstalled('imagemin-mozjpeg', resolvePath) ? 'imagemin-mozjpeg' : false const gif = isModuleInstalled('imagemin-gifsicle', resolvePath) ? 'imagemin-gifsicle' : false const svg = isModuleInstalled('imagemin-svgo', resolvePath) ? 'imagemin-svgo' : false const svgSprite = isModuleInstalled('svg-sprite-loader', resolvePath) ? 'svg-sprite-loader' : false const webp = isModuleInstalled('webp-loader', resolvePath) ? 'webp-loader' : false const lqip = isModuleInstalled('lqip-loader', resolvePath) ? 'lqip-loader' : false let png = false let responsive = false let responsiveAdapter = false if (isModuleInstalled('imagemin-optipng', resolvePath)) { png = 'imagemin-optipng' } else if (isModuleInstalled('imagemin-pngquant', resolvePath)) { png = 'imagemin-pngquant' } if (isModuleInstalled('responsive-loader', resolvePath)) { responsive = require .resolve( 'responsive-loader', resolvePath ? { paths: [resolvePath] } : undefined ) .replace(/(\/|\\)lib(\/|\\)index.js$/g, '') if (isModuleInstalled('sharp', resolvePath)) { responsiveAdapter = 'sharp' } else if (isModuleInstalled('jimp', resolvePath)) { responsiveAdapter = 'jimp' } } return { jpeg, gif, svg, svgSprite, webp, png, lqip, responsive, responsiveAdapter, } } /** * Checks which image types should by handled by this plugin * * @param {object} nextConfig - next.js configuration object * @returns {object} */ const getHandledImageTypes = (nextConfig) => { const { handleImages } = nextConfig return { jpeg: handleImages.indexOf('jpeg') >= 0 || handleImages.indexOf('jpg') >= 0, png: handleImages.indexOf('png') >= 0, svg: handleImages.indexOf('svg') >= 0, webp: handleImages.indexOf('webp') >= 0, gif: handleImages.indexOf('gif') >= 0, ico: handleImages.indexOf('ico') >= 0, } } /** * Returns the number of image optimization loaders installed * * @param {object} loaders - detected loaders * @returns {number} */ const getNumOptimizationLoadersInstalled = (loaders) => Object.values(loaders).filter( (loader) => loader && (loader.startsWith('imagemin-') || loader.startsWith('webp-') || loader.startsWith('lqip-')) ).length /** * Appends all loaders to the webpack configuration * * @param {object} webpackConfig - webpack configuration * @param {object} nextConfig - next.js configuration * @param {object} detectedLoaders - detected loaders * @param {boolean} isServer - if the build is for the server * @param {boolean} optimize - if images should get optimized or just copied * @returns {object} */ const appendLoaders = ( webpackConfig, nextConfig, detectedLoaders, isServer, optimize ) => { let config = webpackConfig const handledImageTypes = getHandledImageTypes(nextConfig) let imgLoaderHandledTypes = handledImageTypes // check if responsive-loader should be the default loader and apply it if so if ( nextConfig.defaultImageLoader && nextConfig.defaultImageLoader === 'responsive-loader' ) { // img-loader no longer has to handle jpeg and png images imgLoaderHandledTypes = { ...imgLoaderHandledTypes, jpeg: false, png: false, } config = applyResponsiveLoader( webpackConfig, nextConfig, isServer, detectLoaders ) } // apply img loader const shouldApplyImgLoader = imgLoaderHandledTypes.jpeg || imgLoaderHandledTypes.png || imgLoaderHandledTypes.gif || imgLoaderHandledTypes.svg if ( (detectedLoaders.jpeg || detectedLoaders.png || detectedLoaders.gif || detectedLoaders.svg) && shouldApplyImgLoader ) { config = applyImgLoader( webpackConfig, nextConfig, optimize, isServer, detectedLoaders, imgLoaderHandledTypes ) } else if (shouldApplyImgLoader) { config = applyImgLoader( webpackConfig, nextConfig, false, isServer, detectedLoaders, imgLoaderHandledTypes ) } // apply webp loader if (detectedLoaders.webp && handledImageTypes.webp) { config = applyWebpLoader( webpackConfig, nextConfig, optimize, isServer, detectLoaders ) } else if (handledImageTypes.webp) { config = applyWebpLoader( webpackConfig, nextConfig, false, isServer, detectLoaders ) } // apply file loader for non optimizable image types if (handledImageTypes.ico) { config = applyFileLoader(webpackConfig, nextConfig, isServer, /\.(ico)$/i) } return config } export { isModuleInstalled, detectLoaders, getHandledImageTypes, getNumOptimizationLoadersInstalled, appendLoaders, }