Skip to content

Commit

Permalink
Merge pull request #257 from dc7290/main
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
dc7290 authored Sep 25, 2022
2 parents cd80d61 + 787d0ad commit 08d654c
Show file tree
Hide file tree
Showing 13 changed files with 391 additions and 236 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This makes it possible to build a high performance website with this solution, w
- Using `sharp`, so it's fast.
- Cache prevents repeating the same optimization
- Support TypeScript
- Support `next/future/image`

## Installation

Expand Down
Binary file added __tests__/e2e/images/future-img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions __tests__/e2e/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fs from 'fs-extra'
const exist = (filename: string) => fs.existsSync(path.resolve(__dirname, 'out/_next/static/chunks/images', filename))

const files = [
// next/image
'_next/static/media/img.8a5ad2fe_1920_75.webp',
'_next/static/media/img.8a5ad2fe_3840_75.webp',
'images/img_640_75.webp',
Expand All @@ -19,6 +20,17 @@ const files = [
'images/img_3840_75.svg',
'og_1920_75.webp',
'og_3840_75.webp',
// next/future/image
'_next/static/media/future-img.8a5ad2fe_1920_75.webp',
'_next/static/media/future-img.8a5ad2fe_3840_75.webp',
'images/future-img_640_75.webp',
'images/future-img_750_75.webp',
'images/future-img_828_75.webp',
'images/future-img_1080_75.webp',
'images/future-img_1200_75.webp',
'images/future-img_1920_75.webp',
'images/future-img_2048_75.webp',
'images/future-img_3840_75.webp',
]

describe('`next build && next export && next-export-optimize-images` is executed correctly', () => {
Expand Down
43 changes: 28 additions & 15 deletions __tests__/e2e/pages/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useEffect, useState } from 'react'

import FutureImage from '../../../dist/future-image'
import Image from '../../../dist/image'
import clientOnlySrc from '../images/client-only.png'
import futureImgSrc from '../images/future-img.png'
import imgSrc from '../images/img.png'

const IndexPage = () => {
Expand All @@ -11,21 +13,32 @@ const IndexPage = () => {
}, [])

return (
<div>
{/* Imported image */}
<Image src={imgSrc} />

{/* Static image */}
<Image src="/images/img.png" width={1920} height={1280} sizes="(min-width: 768px) 720px, 85vw" />

{/* Invalid format image */}
<Image src="/images/img.svg" width={1920} height={1280} />

{/* External image */}
<Image src="https://next-export-optimize-images.vercel.app/og.png" width={1920} height={1280} />

{isClient && <Image src={clientOnlySrc} />}
</div>
<>
{/* next/image */}
<div>
{/* Imported image */}
<Image src={imgSrc} />

{/* Static image */}
<Image src="/images/img.png" width={1920} height={1280} sizes="(min-width: 768px) 720px, 85vw" />

{/* Invalid format image */}
<Image src="/images/img.svg" width={1920} height={1280} />

{/* External image */}
<Image src="https://next-export-optimize-images.vercel.app/og.png" width={1920} height={1280} />

{isClient && <Image src={clientOnlySrc} />}
</div>
{/* next/future/image */}
<div>
{/* Imported image */}
<FutureImage src={futureImgSrc} />

{/* Static image */}
<FutureImage src="/images/future-img.png" width={1920} height={1280} sizes="(min-width: 768px) 720px, 85vw" />
</div>
</>
)
}

Expand Down
Binary file added __tests__/e2e/public/images/future-img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/docs/01-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ This makes it possible to build a high performance website with this solution, w
- Using `sharp`, so it's fast.
- Cache prevents repeating the same optimization
- Support TypeScript
- Support `next/future/image`
7 changes: 7 additions & 0 deletions docs/docs/02-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ module.exports = withPlugins(
<Image src={require('./img.png')} alt="" />
```

Alternatively, you can use `next/future/image`.

```jsx
import Image from 'next/future/image'
;<Image src="/images/img.png" width={1920} height={1280} alt="" />
```

## Local checks

1. Run `yarn export`.
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@docusaurus/preset-classic": "2.1.0",
"@heroicons/react": "1.0.6",
"@mdx-js/react": "1.6.22",
"autoprefixer": "10.4.11",
"autoprefixer": "10.4.12",
"clsx": "1.2.1",
"postcss": "8.4.16",
"prettier": "2.7.1",
Expand Down
55 changes: 25 additions & 30 deletions docs/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3404,13 +3404,13 @@ at-least-node@^1.0.0:
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==

[email protected].11:
version "10.4.11"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.11.tgz#835136aff1d9cd43640151e0d2dba00f8eac7c1c"
integrity sha512-5lHp6DgRodxlBLSkzHOTcufWFflH1ewfy2hvFQyjrblBFlP/0Yh4O/Wrg4ow8WRlN3AAUFFLAQwX8hTptzqVHg==
[email protected].12:
version "10.4.12"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.12.tgz#183f30bf0b0722af54ee5ef257f7d4320bb33129"
integrity sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==
dependencies:
browserslist "^4.21.3"
caniuse-lite "^1.0.30001399"
browserslist "^4.21.4"
caniuse-lite "^1.0.30001407"
fraction.js "^4.2.0"
normalize-range "^0.1.2"
picocolors "^1.0.0"
Expand Down Expand Up @@ -3608,15 +3608,15 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4
node-releases "^2.0.3"
picocolors "^1.0.0"

browserslist@^4.21.3:
version "4.21.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==
browserslist@^4.21.4:
version "4.21.4"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
dependencies:
caniuse-lite "^1.0.30001370"
electron-to-chromium "^1.4.202"
caniuse-lite "^1.0.30001400"
electron-to-chromium "^1.4.251"
node-releases "^2.0.6"
update-browserslist-db "^1.0.5"
update-browserslist-db "^1.0.9"

buffer-from@^1.0.0:
version "1.1.2"
Expand Down Expand Up @@ -3692,15 +3692,10 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001335:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001340.tgz#029a2f8bfc025d4820fafbfaa6259fd7778340c7"
integrity sha512-jUNz+a9blQTQVu4uFcn17uAD8IDizPzQkIKh3LCJfg9BkyIqExYYdyc/ZSlWUSKb8iYiXxKsxbv4zYSvkqjrxw==

caniuse-lite@^1.0.30001370:
version "1.0.30001373"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be"
integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==

caniuse-lite@^1.0.30001399:
version "1.0.30001402"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz#aa29e1f47f5055b0d0c07696a67b8b08023d14c8"
integrity sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==
caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001407:
version "1.0.30001410"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz#b5a86366fbbf439d75dd3db1d21137a73e829f44"
integrity sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==

ccount@^1.0.0:
version "1.1.0"
Expand Down Expand Up @@ -4530,10 +4525,10 @@ electron-to-chromium@^1.4.118:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f"
integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==

electron-to-chromium@^1.4.202:
version "1.4.206"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz#580ff85b54d7ec0c05f20b1e37ea0becdd7b0ee4"
integrity sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA==
electron-to-chromium@^1.4.251:
version "1.4.260"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.260.tgz#9aa3348d037686b47ccc5d3b48fe417b1f20017a"
integrity sha512-1GxPM2Bdz1AjuNjho9/TqJfxM7KZ7R8s4vA5cbbIoVacQXfvZlV+d7Y1lu4BhGzEBfjjhakr3NXKqN0PxPXIsg==

emoji-regex@^8.0.0:
version "8.0.0"
Expand Down Expand Up @@ -8373,10 +8368,10 @@ [email protected], unpipe@~1.0.0:
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=

update-browserslist-db@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38"
integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==
update-browserslist-db@^1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18"
integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==
dependencies:
escalade "^3.1.1"
picocolors "^1.0.0"
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,26 +66,26 @@
"@semantic-release/changelog": "6.0.1",
"@semantic-release/git": "10.0.1",
"@swc/cli": "0.1.57",
"@swc/core": "1.3.1",
"@swc/core": "1.3.3",
"@testing-library/react": "13.4.0",
"@testing-library/jest-dom": "5.16.5",
"@tsconfig/strictest": "1.0.1",
"@tsconfig/strictest": "1.0.2",
"@types/app-root-path": "1.2.4",
"@types/benchmark": "2.1.2",
"@types/cli-progress": "3.11.0",
"@types/fs-extra": "9.0.13",
"@types/jest": "27.5.2",
"@types/lodash.uniqwith": "4.5.7",
"@types/node": "16.11.59",
"@types/react": "18.0.20",
"@types/node": "16.11.60",
"@types/react": "18.0.21",
"@types/sharp": "0.31.0",
"@typescript-eslint/eslint-plugin": "5.37.0",
"@typescript-eslint/parser": "5.37.0",
"benchmark": "2.1.4",
"chokidar": "3.5.3",
"conventional-changelog-conventionalcommits": "5.0.0",
"cross-env": "7.0.3",
"eslint": "8.23.1",
"eslint": "8.24.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jest": "26.9.0",
Expand All @@ -96,7 +96,7 @@
"jest": "28.1.3",
"jest-environment-jsdom": "28.1.3",
"lint-staged": "13.0.3",
"next": "12.3.0",
"next": "12.3.1",
"npm-run-all": "4.1.5",
"prettier": "2.7.1",
"react": "18.2.0",
Expand All @@ -116,7 +116,7 @@
"node": "^14.0.0 || ^16.0.0 || ^18.0.0"
},
"volta": {
"node": "16.17.0",
"node": "16.17.1",
"yarn": "1.22.19"
}
}
120 changes: 120 additions & 0 deletions src/future-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import Image, { ImageLoader, ImageProps } from 'next/dist/client/future/image'
import React from 'react'

import type { Manifest } from './cli/types'
import formatValidate from './cli/utils/formatValidate'
import getConfig, { ParsedImageInfo } from './utils/getConfig'

const config = getConfig()

const defaultImageParser: (src: string) => ParsedImageInfo = (src: string) => {
const path = src.split(/\.([^.]*$)/)[0]
const extension = src.split(/\.([^.]*$)/)[1]

if (!path || !extension) {
throw new Error(`Invalid path or no file extension: ${src}`)
}

let pathWithoutName = path.split('/').slice(0, -1).join('/')
const name = path.split('/').slice(-1).toString()

if (src.startsWith('http')) {
pathWithoutName = pathWithoutName
.replace(/^https?:\/\//, '')
.split('/')
.slice(1)
.join('/')
}

return {
pathWithoutName,
name,
extension,
}
}

const exportableLoader: ImageLoader = ({ src: _src, width, quality }) => {
if (process.env.NODE_ENV === 'development') {
// This doesn't bother optimizing in the dev environment. Next complains if the
// returned URL doesn't have a width in it, so adding it as a throwaway
return `${_src}?width=${width}`
}

let src = _src

if (config.basePath !== undefined) {
src = _src.replace(config.basePath, '')
}

const parsedImageInformation = config.sourceImageParser
? config.sourceImageParser({ src, defaultParser: defaultImageParser })
: defaultImageParser(src)

let { extension } = parsedImageInformation
const { pathWithoutName, name } = parsedImageInformation

if (config.convertFormat !== undefined) {
const convertArray = config.convertFormat.find(([beforeConvert]) => beforeConvert === extension)
if (convertArray !== undefined) {
if (!formatValidate(convertArray[0]))
throw Error(`Unauthorized format specified in \`configFormat\`. beforeConvert: ${convertArray[0]}`)
if (!formatValidate(convertArray[1]))
throw Error(`Unauthorized format specified in \`configFormat\`. afterConvert: ${convertArray[1]}`)

extension = convertArray[1]
}
}

const outputDir = `/${
config.imageDir ? config.imageDir.replace(/^\//, '').replace(/\/$/, '') : '_next/static/chunks/images'
}`
const externalOutputDir = `${
config.externalImageDir ? config.externalImageDir.replace(/^\//, '').replace(/\/$/, '') : '_next/static/media'
}`
const filename =
config.filenameGenerator !== undefined
? config.filenameGenerator({ path: pathWithoutName, name, width, quality: quality || 75, extension })
: `${pathWithoutName}/${name}_${width}_${quality || 75}.${extension}`
const output = `${outputDir}/${filename.replace(/^\//, '')}`

if (typeof window === 'undefined' || process.env['TEST_JSON_PATH'] !== undefined) {
const json: Manifest[number] = { output, src, width, quality: quality || 75, extension }
const fs = require('fs-extra') as typeof import('fs-extra')
const path = require('path') as typeof import('path')

if (src.startsWith('http')) {
json.src = `/${externalOutputDir}/${src
.replace(/^https?:\/\//, '')
.split('/')
.slice(1)
.join('/')}`
json.externalUrl = src
}

fs.appendFileSync(
path.join(process.cwd(), process.env['TEST_JSON_PATH'] ?? '.next/custom-optimized-images.nd.json'),
JSON.stringify(json) + '\n'
)
}

return `${config.basePath ?? ''}${output}`
}

const CustomImage = (props: ImageProps) => {
return (
<Image
{...props}
loader={props.loader || exportableLoader}
blurDataURL={
props.blurDataURL ||
(typeof props.src === 'string' && props.placeholder === 'blur' && props.loader === undefined
? exportableLoader({ src: props.src, width: 8, quality: 10 })
: '')
}
/>
)
}

export * from 'next/dist/client/future/image'
export default CustomImage
1 change: 1 addition & 0 deletions src/withExportImages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const withExportImages = (nextConfig: NextConfig = {}, options?: Options): NextC
]
} else {
config.resolve.alias['next/image'] = 'next-export-optimize-images/dist/image'
config.resolve.alias['next/future/image'] = 'next-export-optimize-images/dist/future-image'
delete config.resolve.alias['next']
}

Expand Down
Loading

0 comments on commit 08d654c

Please sign in to comment.