diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 83d530a8..78618342 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - site: [astro, lit, preact, qwik, react, solid, svelte, webc] + site: [astro, lit, preact, qwik, react, solid, webc] steps: - run: echo Running tests in ${{ matrix.site }} - name: Checkout diff --git a/docs/src/content/img/react.mdx b/docs/src/content/img/react.mdx index cfb47a3e..ccb84c9a 100644 --- a/docs/src/content/img/react.mdx +++ b/docs/src/content/img/react.mdx @@ -79,7 +79,7 @@ import logo from "../public/logo.png"; /> ``` -> ⚠️ For versions of Next.js before 14.0.0, import from +> ⚠️ For versions of Next.js before 13.5, import from > `@unpic/react/next-legacy`, not `@unpic/react/nextjs` ### Differences from `next/image` diff --git a/examples/nextjs/app/layout.tsx b/examples/nextjs/app/layout.tsx new file mode 100644 index 00000000..1c43d2ea --- /dev/null +++ b/examples/nextjs/app/layout.tsx @@ -0,0 +1,16 @@ +export const metadata = { + title: "Next.js", + description: "Generated by Next.js", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/examples/nextjs/app/rsc/page.tsx b/examples/nextjs/app/rsc/page.tsx new file mode 100644 index 00000000..c472abb1 --- /dev/null +++ b/examples/nextjs/app/rsc/page.tsx @@ -0,0 +1,45 @@ +import Head from "next/head"; +import { Image } from "@unpic/react/nextjs"; +export default function Home() { + return ( + <> + + Create Next App + + + + +
+ fullWidth + constrained + fixed +
+ offscreen +
+ + ); +} diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 021ebafe..88bbd06e 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -14,11 +14,11 @@ "@types/react-dom": "^18.2.22", "@unpic/react": "workspace:^", "next": "^14.1.3", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "typescript": "^5.4.2" }, "devDependencies": { - "@netlify/plugin-nextjs": "5.0.0-rc.1" + "@netlify/plugin-nextjs": "^5.0.0" } } \ No newline at end of file diff --git a/examples/nextjs/tsconfig.json b/examples/nextjs/tsconfig.json index 8b8e5811..279f94bf 100644 --- a/examples/nextjs/tsconfig.json +++ b/examples/nextjs/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -15,9 +19,23 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./*"] - } + "@/*": [ + "./*" + ] + }, + "plugins": [ + { + "name": "next" + } + ] }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/packages/react/README.md b/packages/react/README.md index ede7c6d6..a0278a08 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -87,7 +87,7 @@ import logo from "../public/logo.png"; ; ``` -> ⚠️ For versions of Next.js before 14.0.0, import from +> ⚠️ For versions of Next.js before 13.5, import from > `@unpic/react/next-legacy`, not `@unpic/react/nextjs` ### Differences from `next/image` diff --git a/packages/react/package.json b/packages/react/package.json index 40c26276..14b1abd8 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -93,7 +93,7 @@ "@unpic/core": "0.0.49" }, "peerDependencies": { - "next": "^14.0.0", + "next": "^13.0.0 || ^14.0.0", "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0" }, diff --git a/packages/react/src/nextjs.tsx b/packages/react/src/nextjs.tsx index c0a8985f..0733f781 100644 --- a/packages/react/src/nextjs.tsx +++ b/packages/react/src/nextjs.tsx @@ -1,10 +1,11 @@ -import { useContext, forwardRef, useMemo, useEffect } from "react"; +import { forwardRef, useMemo } from "react"; import type { ImageProps as UnpicImageProps } from "./index"; import { Image as UnpicImage } from "./index"; import { getImageCdnForUrl } from "unpic"; -import type { ImageConfigComplete } from "next/dist/shared/lib/image-config.js"; -import { imageConfigDefault } from "next/dist/shared/lib/image-config.js"; -import { ImageConfigContext } from "next/dist/shared/lib/image-config-context.shared-runtime.js"; +import { + imageConfigDefault, + type ImageConfigComplete, +} from "next/dist/shared/lib/image-config.js"; // const configEnv = process.env @@ -32,37 +33,6 @@ export type ImageProps = Omit & { src: string | StaticImport; }; -function checkMatchingPatterns(config: ImageConfigComplete, src: string) { - if ( - // match-remote-pattern doesn't support the edge runtime - process.env.NEXT_RUNTIME === "edge" || - // we don't have access to the image domains/remotePatterns in production - process.env.NODE_ENV !== "development" - ) { - return; - } - - if (!src?.startsWith("http://") && !src?.startsWith("https://")) { - return; - } - let parsedSrc: URL; - try { - parsedSrc = new URL(src); - } catch (err) { - console.error(err); - return; - } - - import("next/dist/shared/lib/match-remote-pattern").then(({ hasMatch }) => { - if (!hasMatch(config.domains, config.remotePatterns, parsedSrc)) { - throw new Error( - `[Unpic]: Invalid src (${src}). Images that aren't on a supported image CDN must be configured under images in your \`next.config.js\`\n` + - `See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host`, - ); - } - }); -} - // Next.js allows various different shapes of the src prop function getImageData(src: string | StaticImport): StaticImageData | void { if (typeof src === "string") { @@ -79,8 +49,7 @@ export const Image = forwardRef( // If using the next/image server we can only serve images with // the same breakpoints as those in the config - const configContext = useContext(ImageConfigContext); - const config = configEnv || configContext || imageConfigDefault; + const config = configEnv || imageConfigDefault; const breakpoints = useMemo(() => { return [...config.deviceSizes, ...config.imageSizes]; }, [config]); @@ -131,13 +100,6 @@ export const Image = forwardRef( const isRemoteCdn = cdn && cdn !== "nextjs" && cdn !== "vercel"; - useEffect(() => { - if (!src || !config || isRemoteCdn) { - return; - } - checkMatchingPatterns(config, src); - }, [src, isRemoteCdn, config]); - // Other image CDNs can use normal Unpic breakpoints if (isRemoteCdn) { return ; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92a85ad8..47ff9e47 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,7 +80,7 @@ importers: version: 2.3.1(astro@4.7.0) '@astrojs/preact': specifier: ^3.2.0 - version: 3.2.0(@babel/core@7.24.3)(preact@10.20.2)(vite@5.2.10) + version: 3.2.0(@babel/core@7.24.4)(preact@10.20.2)(vite@5.2.10) '@astrojs/react': specifier: ^3.3.1 version: 3.3.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(vite@5.2.10) @@ -266,20 +266,20 @@ importers: version: link:../../packages/react next: specifier: ^14.1.3 - version: 14.1.3(react-dom@18.2.0)(react@18.2.0) + version: 14.1.3(react-dom@18.3.1)(react@18.3.1) react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) typescript: specifier: ^5.4.2 version: 5.4.2 devDependencies: '@netlify/plugin-nextjs': - specifier: 5.0.0-rc.1 - version: 5.0.0-rc.1 + specifier: ^5.0.0 + version: 5.1.2 examples/preact: dependencies: @@ -1890,17 +1890,17 @@ packages: - supports-color dev: false - /@astrojs/preact@3.2.0(@babel/core@7.24.3)(preact@10.20.2)(vite@5.2.10): + /@astrojs/preact@3.2.0(@babel/core@7.24.4)(preact@10.20.2)(vite@5.2.10): resolution: {integrity: sha512-p49NO/EgMbO5tCugFYXPQVAJelww46hXt947uGjXC8IchslS4MNqj4lH0ZzNGeeK1hRKXovz7/LK6AXJgMxleA==} engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0} peerDependencies: preact: ^10.6.5 dependencies: - '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.3) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.3) - '@preact/preset-vite': 2.8.2(@babel/core@7.24.3)(preact@10.20.2)(vite@5.2.10) + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.4) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.4) + '@preact/preset-vite': 2.8.2(@babel/core@7.24.4)(preact@10.20.2)(vite@5.2.10) '@preact/signals': 1.2.3(preact@10.20.2) - babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.3) + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.4) preact: 10.20.2 preact-render-to-string: 6.3.1(preact@10.20.2) preact-ssr-prepass: 1.2.1(preact@10.20.2) @@ -3241,14 +3241,14 @@ packages: '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.1) dev: true - /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.24.3): + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.24.4): resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.3) + '@babel/core': 7.24.4 + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.4) dev: false /@babel/plugin-transform-react-jsx-self@7.24.1(@babel/core@7.24.1): @@ -6212,11 +6212,6 @@ packages: engines: {node: '>=14'} dev: true - /@netlify/plugin-nextjs@5.0.0-rc.1: - resolution: {integrity: sha512-VrGZo0z2OVChgQWuVinVNFXycSVfvvzcnMVNuoi9cSyFCyoV0zETF4gRgb+uDjq3zio0Z9eKkM56CC7p3vaQ+A==} - engines: {node: '>=18.0.0'} - dev: true - /@netlify/plugin-nextjs@5.1.2: resolution: {integrity: sha512-rftBlZmrkxy8zeXZ/tT2u/v2iIZd9s0cV7MdtjUFOgizjhtY0fCZEUQ7OIEQIMogzGNMDXFcb84HXX9a8OZ8/w==} engines: {node: '>=18.0.0'} @@ -8263,18 +8258,18 @@ packages: - supports-color dev: true - /@preact/preset-vite@2.8.2(@babel/core@7.24.3)(preact@10.20.2)(vite@5.2.10): + /@preact/preset-vite@2.8.2(@babel/core@7.24.4)(preact@10.20.2)(vite@5.2.10): resolution: {integrity: sha512-m3tl+M8IO8jgiHnk+7LSTFl8axdPXloewi7iGVLdmCwf34XOzEUur0bZVewW4DUbUipFjTS2CXu27+5f/oexBA==} peerDependencies: '@babel/core': 7.x vite: 2.x || 3.x || 4.x || 5.x dependencies: - '@babel/core': 7.24.3 - '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.3) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.3) + '@babel/core': 7.24.4 + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.4) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.4) '@prefresh/vite': 2.4.5(preact@10.20.2)(vite@5.2.10) '@rollup/pluginutils': 4.2.1 - babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.3) + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.4) debug: 4.3.4(supports-color@9.4.0) kolorist: 1.8.0 magic-string: 0.30.5 @@ -11629,12 +11624,12 @@ packages: '@babel/core': 7.24.1 dev: true - /babel-plugin-transform-hook-names@1.0.2(@babel/core@7.24.3): + /babel-plugin-transform-hook-names@1.0.2(@babel/core@7.24.4): resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} peerDependencies: '@babel/core': ^7.12.10 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.4 dev: false /babel-preset-solid@1.8.15(@babel/core@7.24.1): @@ -19684,7 +19679,7 @@ packages: type-fest: 2.19.0 dev: true - /next@14.1.3(react-dom@18.2.0)(react@18.2.0): + /next@14.1.3(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==} engines: {node: '>=18.17.0'} hasBin: true @@ -19705,9 +19700,9 @@ packages: caniuse-lite: 1.0.30001599 graceful-fs: 4.2.11 postcss: 8.4.31 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.24.1)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 14.1.3 '@next/swc-darwin-x64': 14.1.3 @@ -24329,24 +24324,6 @@ packages: '@babel/core': 7.24.1 client-only: 0.0.1 react: 18.3.1 - dev: true - - /styled-jsx@5.1.1(react@18.2.0): - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - dependencies: - client-only: 0.0.1 - react: 18.2.0 - dev: false /stylehacks@6.1.0(postcss@8.4.38): resolution: {integrity: sha512-ETErsPFgwlfYZ/CSjMO2Ddf+TsnkCVPBPaoB99Ro8WMAxf7cglzmFsRBhRmKObFjibtcvlNxFFPHuyr3sNlNUQ==}