Open JuanM04 opened 4 years ago
You need to use babel compiler for replace param of image at the place of the component call
babel-plugin-replace-path-images.js
var babelParser = require('@babel/parser')
const nestedVisitor = {
StringLiteral(path, opts) {
try{
var pathFile = path.node.value;
if(pathFile && /\_\_load\_images\_\_/g.test(pathFile)){
console.log('optimize image', pathFile)
var newPath = pathFile.replace('__load_images__', ''),
originPath = newPath;
var pathRequest = newPath.match(/\?.*/g);
if(pathRequest){
pathRequest = pathRequest[0];
newPath = newPath.replace(pathRequest, '')
}
if( !newPath ) return match;
var resizeMode = pathRequest && /\\?resize/.test(pathRequest),
webpMode = pathRequest && /\\?webp/.test(pathRequest),
images = [], imagesString = '[';
if(resizeMode){
images.push(`require('public/static/${newPath}?resize&format=webp')`)
if(/\.png/g.test(newPath)){
images.push(`require('public/static/${newPath}?resize&format=png')`);
}
if(/\.jpg/g.test(newPath)){
images.push(`require('public/static/${newPath}?resize&format=jpg')`);
}
}
if(webpMode){
images.push(`{path: require('public/static/${newPath}?webp')}`)
images.push(`{path: require('public/static/${newPath}')}`)
}
if(!webpMode && !resizeMode){
images.push(`{path: require('public/static/${originPath}')}`)
}
for(var item of images){
imagesString += item + ','
}
imagesString += ']';
var node = babelParser.parse(imagesString)
path.replaceWith(
node.program.body[0]
);
}
}catch(err){
console.log('err', err)
}
}
}
const nestedVisitor2 = {
JSXExpressionContainer(path, opts){
path.traverse(nestedVisitor, opts);
}
}
module.exports = function({ types: t }) {
return {
visitor: {
JSXAttribute(path) {
path.traverse(nestedVisitor2, t);
}
}
};
}
then apply plugin in babel.config.js
require('dotenv').config();
var DEV_VAR = process.env.DEV_VAR == 'true' ? false : true;
module.exports = function (api) {
api.cache(DEV_VAR);
return {
presets: [
["next/babel"]
],
"plugins": [
["./babel-plugin-replace-path-images.js"]
]
}
}
It also works correctly on devices that do not support the webp image format.
My realization of image wrapper component -
import { Component } from 'react';
class Img extends Component {
componentDidMount() {
if (this.img.complete) {
this.img.style.background = 'initial';
}
}
render() {
return(
<img
ref={el => this.img = el}
onLoad={ e => {
e.target.style.background = 'initial'
}}
{...this.props}
/>
)
}
}
const Image = (props) => {
if(!props.src) return null;
var newProps = JSON.parse(JSON.stringify(props))
var images = [], image, placeholder, addExtra = false;
for(var item of props.src){
if(item.images){
if(!addExtra){
addExtra = true;
image = item.image;
placeholder = item.placeholder;
}
for(var item2 of item.images){
images.push(item2)
}
}else{
images.push(item)
}
}
var reversed = images.reverse();
delete newProps.src;
delete newProps.require;
delete newProps.priority;
if(placeholder){
newProps.style = {background: `url(${placeholder})`}
}
if(!props.priority){
newProps.loading = "lazy"
}
return(
<picture>
{reversed.map(item => {
var sourceProps = {
srcSet: item.path,
type: 'image/'
}
if(/(\.png|data\:image\/png\;base64)/g.test(item.path)){
sourceProps.type += 'png'
}else if(/(\.webp|data\:image\/webp\;base64)/g.test(item.path)){
sourceProps.type += 'webp'
}else if(/(\.jpg|data\:image\/jpg\;base64)/g.test(item.path)){
sourceProps.type += 'jpeg'
}else{
delete sourceProps.type;
}
if(item.width){
sourceProps.media = `(min-width: ${item.width - 10}px)`
}
return(
<source {...sourceProps} />
)
})}
<Img
src={image}
{...newProps}
/>
</picture>
)
};
export default Image;
finally example how you can use it
<Image draggable="false" src={"__load_images__happyDeveloper.png?resize"} alt="Счастливый разработчик" />
```You need to use babel compiler for replace param of image at the place of the component call
> babel-plugin-replace-path-images.js
```JavaScript
var babelParser = require('@babel/parser')
const nestedVisitor = {
StringLiteral(path, opts) {
try{
var pathFile = path.node.value;
if(pathFile && /\_\_load\_images\_\_/g.test(pathFile)){
console.log('optimize image', pathFile)
var newPath = pathFile.replace('__load_images__', ''),
originPath = newPath;
var pathRequest = newPath.match(/\?.*/g);
if(pathRequest){
pathRequest = pathRequest[0];
newPath = newPath.replace(pathRequest, '')
}
if( !newPath ) return match;
var resizeMode = pathRequest && /\\?resize/.test(pathRequest),
webpMode = pathRequest && /\\?webp/.test(pathRequest),
images = [], imagesString = '[';
if(resizeMode){
images.push(`require('public/static/${newPath}?resize&format=webp')`)
if(/\.png/g.test(newPath)){
images.push(`require('public/static/${newPath}?resize&format=png')`);
}
if(/\.jpg/g.test(newPath)){
images.push(`require('public/static/${newPath}?resize&format=jpg')`);
}
}
if(webpMode){
images.push(`{path: require('public/static/${newPath}?webp')}`)
images.push(`{path: require('public/static/${newPath}')}`)
}
if(!webpMode && !resizeMode){
images.push(`{path: require('public/static/${originPath}')}`)
}
for(var item of images){
imagesString += item + ','
}
imagesString += ']';
var node = babelParser.parse(imagesString)
path.replaceWith(
node.program.body[0]
);
}
}catch(err){
console.log('err', err)
}
}
}
const nestedVisitor2 = {
JSXExpressionContainer(path, opts){
path.traverse(nestedVisitor, opts);
}
}
module.exports = function({ types: t }) {
return {
visitor: {
JSXAttribute(path) {
path.traverse(nestedVisitor2, t);
}
}
};
}
then apply plugin in babel.config.js
require('dotenv').config();
var DEV_VAR = process.env.DEV_VAR == 'true' ? false : true;
module.exports = function (api) {
api.cache(DEV_VAR);
return {
presets: [
["next/babel"]
],
"plugins": [
["./babel-plugin-replace-path-images.js"]
]
}
}
It also works correctly on devices that do not support the webp image format.
My realization of image wrapper component -
import { Component } from 'react';
class Img extends Component {
componentDidMount() {
if (this.img.complete) {
this.img.style.background = 'initial';
}
}
render() {
return(
<img
ref={el => this.img = el}
onLoad={ e => {
e.target.style.background = 'initial'
}}
{...this.props}
/>
)
}
}
const Image = (props) => {
if(!props.src) return null;
var newProps = JSON.parse(JSON.stringify(props))
var images = [], image, placeholder, addExtra = false;
for(var item of props.src){
if(item.images){
if(!addExtra){
addExtra = true;
image = item.image;
placeholder = item.placeholder;
}
for(var item2 of item.images){
images.push(item2)
}
}else{
images.push(item)
}
}
var reversed = images.reverse();
delete newProps.src;
delete newProps.require;
delete newProps.priority;
if(placeholder){
newProps.style = {background: `url(${placeholder})`}
}
if(!props.priority){
newProps.loading = "lazy"
}
return(
<picture>
{reversed.map(item => {
var sourceProps = {
srcSet: item.path,
type: 'image/'
}
if(/(\.png|data\:image\/png\;base64)/g.test(item.path)){
sourceProps.type += 'png'
}else if(/(\.webp|data\:image\/webp\;base64)/g.test(item.path)){
sourceProps.type += 'webp'
}else if(/(\.jpg|data\:image\/jpg\;base64)/g.test(item.path)){
sourceProps.type += 'jpeg'
}else{
delete sourceProps.type;
}
if(item.width){
sourceProps.media = `(min-width: ${item.width - 10}px)`
}
return(
<source {...sourceProps} />
)
})}
<Img
src={image}
{...newProps}
/>
</picture>
)
};
export default Image;
finally example how you can use it
<Image draggable="false" src={"__load_images__happyDeveloper.png?resize"} alt="Счастливый разработчик" />
When I require an image from
/pages
, it works perfectly; but when I do it from/components
, it throws "Can't find module [path]".Next version: 9.5.3 Next-optimized-images version: 2.6.2 Typescript version: 4.0.2