Next.js 16 (#351)
* Upgrade to next.js 16 * Allow static generation on preview * Add note for disabled ref rule * Report Next.js version in App Insights * Link Next.js version
This commit is contained in:
parent
3f1a36354d
commit
c8ea51cdd1
@ -18,6 +18,7 @@ const eslintConfig = defineConfig([
|
|||||||
'@stylistic': stylistic,
|
'@stylistic': stylistic,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
// Disable rule during Next.js 16 migration
|
||||||
'react-hooks/refs': 'off',
|
'react-hooks/refs': 'off',
|
||||||
'@next/next/no-img-element': 'off',
|
'@next/next/no-img-element': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
|||||||
30
package.json
30
package.json
@ -8,12 +8,12 @@
|
|||||||
"test": "jest --watch --transformIgnorePatterns 'node_modules/(?!my-library-dir)/'",
|
"test": "jest --watch --transformIgnorePatterns 'node_modules/(?!my-library-dir)/'",
|
||||||
"analyze": "ANALYZE=true next build"
|
"analyze": "ANALYZE=true next build"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.19.0",
|
"packageManager": "pnpm@10.20.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^2.0.54",
|
"@ai-sdk/openai": "^2.0.59",
|
||||||
"@ai-sdk/rsc": "^1.0.81",
|
"@ai-sdk/rsc": "^1.0.86",
|
||||||
"@aws-sdk/client-s3": "3.917.0",
|
"@aws-sdk/client-s3": "3.922.0",
|
||||||
"@aws-sdk/s3-request-presigner": "3.917.0",
|
"@aws-sdk/s3-request-presigner": "3.922.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
@ -24,8 +24,8 @@
|
|||||||
"@vercel/analytics": "^1.5.0",
|
"@vercel/analytics": "^1.5.0",
|
||||||
"@vercel/blob": "^2.0.0",
|
"@vercel/blob": "^2.0.0",
|
||||||
"@vercel/speed-insights": "^1.2.0",
|
"@vercel/speed-insights": "^1.2.0",
|
||||||
"ai": "^5.0.81",
|
"ai": "^5.0.86",
|
||||||
"camelcase-keys": "^10.0.0",
|
"camelcase-keys": "^10.0.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"culori": "^4.0.2",
|
"culori": "^4.0.2",
|
||||||
@ -37,8 +37,8 @@
|
|||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"framer-motion": "^12.23.24",
|
"framer-motion": "^12.23.24",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"next": "15.5.5",
|
"next": "16.0.1",
|
||||||
"next-auth": "5.0.0-beta.29",
|
"next-auth": "5.0.0-beta.30",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"ol": "^10.6.1",
|
"ol": "^10.6.1",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
@ -58,8 +58,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@next/bundle-analyzer": "15.5.5",
|
"@next/bundle-analyzer": "16.0.1",
|
||||||
"@next/eslint-plugin-next": "15.5.5",
|
"@next/eslint-plugin-next": "16.0.1",
|
||||||
"@stylistic/eslint-plugin": "^5.5.0",
|
"@stylistic/eslint-plugin": "^5.5.0",
|
||||||
"@tailwindcss/postcss": "^4.1.16",
|
"@tailwindcss/postcss": "^4.1.16",
|
||||||
"@testing-library/dom": "^10.4.1",
|
"@testing-library/dom": "^10.4.1",
|
||||||
@ -67,14 +67,14 @@
|
|||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@types/culori": "^4.0.1",
|
"@types/culori": "^4.0.1",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^24.9.1",
|
"@types/node": "^24.9.2",
|
||||||
"@types/pg": "^8.15.5",
|
"@types/pg": "^8.15.6",
|
||||||
"@types/react": "19.2.2",
|
"@types/react": "19.2.2",
|
||||||
"@types/react-dom": "19.2.2",
|
"@types/react-dom": "19.2.2",
|
||||||
"@types/sanitize-html": "^2.16.0",
|
"@types/sanitize-html": "^2.16.0",
|
||||||
"cross-fetch": "^4.1.0",
|
"cross-fetch": "^4.1.0",
|
||||||
"eslint": "9.38.0",
|
"eslint": "9.39.0",
|
||||||
"eslint-config-next": "15.5.5",
|
"eslint-config-next": "16.0.1",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
"jest-environment-jsdom": "^30.2.0",
|
"jest-environment-jsdom": "^30.2.0",
|
||||||
"postcss": "8.5.6",
|
"postcss": "8.5.6",
|
||||||
|
|||||||
1649
pnpm-lock.yaml
generated
1649
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@ import {
|
|||||||
PREFIX_TAG,
|
PREFIX_TAG,
|
||||||
} from './src/app/path';
|
} from './src/app/path';
|
||||||
|
|
||||||
export default function middleware(req: NextRequest, res:NextResponse) {
|
export function proxy(req: NextRequest, res:NextResponse) {
|
||||||
const pathname = req.nextUrl.pathname;
|
const pathname = req.nextUrl.pathname;
|
||||||
|
|
||||||
if (pathname === PATH_ADMIN) {
|
if (pathname === PATH_ADMIN) {
|
||||||
@ -11,6 +11,7 @@ import {
|
|||||||
import AdminAppInsightsClient from './AdminAppInsightsClient';
|
import AdminAppInsightsClient from './AdminAppInsightsClient';
|
||||||
import { getAllInsights, getGitHubMetaForCurrentApp } from '.';
|
import { getAllInsights, getGitHubMetaForCurrentApp } from '.';
|
||||||
import { USED_DEPRECATED_ENV_VARS } from '@/app/config';
|
import { USED_DEPRECATED_ENV_VARS } from '@/app/config';
|
||||||
|
import { dependencies } from '../../../package.json';
|
||||||
|
|
||||||
export default async function AdminAppInsights() {
|
export default async function AdminAppInsights() {
|
||||||
const [
|
const [
|
||||||
@ -42,6 +43,7 @@ export default async function AdminAppInsights() {
|
|||||||
return (
|
return (
|
||||||
<AdminAppInsightsClient
|
<AdminAppInsightsClient
|
||||||
codeMeta={codeMeta}
|
codeMeta={codeMeta}
|
||||||
|
nextVersion={dependencies.next}
|
||||||
insights={getAllInsights({
|
insights={getAllInsights({
|
||||||
codeMeta,
|
codeMeta,
|
||||||
photosCount,
|
photosCount,
|
||||||
|
|||||||
@ -48,6 +48,8 @@ import IconPhoto from '@/components/icons/IconPhoto';
|
|||||||
import { HiOutlineDocumentText } from 'react-icons/hi';
|
import { HiOutlineDocumentText } from 'react-icons/hi';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import MaskedScroll from '@/components/MaskedScroll';
|
import MaskedScroll from '@/components/MaskedScroll';
|
||||||
|
import IconNext from '@/components/icons/IconNext';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
const DEBUG_COMMIT_SHA = '4cd29ed';
|
const DEBUG_COMMIT_SHA = '4cd29ed';
|
||||||
const DEBUG_COMMIT_MESSAGE = 'Long commit message for debugging purposes';
|
const DEBUG_COMMIT_MESSAGE = 'Long commit message for debugging purposes';
|
||||||
@ -113,6 +115,7 @@ const renderWarningIconSmall =
|
|||||||
|
|
||||||
export default function AdminAppInsightsClient({
|
export default function AdminAppInsightsClient({
|
||||||
codeMeta,
|
codeMeta,
|
||||||
|
nextVersion,
|
||||||
insights,
|
insights,
|
||||||
usedDeprecatedEnvVars,
|
usedDeprecatedEnvVars,
|
||||||
photoStats: {
|
photoStats: {
|
||||||
@ -129,6 +132,7 @@ export default function AdminAppInsightsClient({
|
|||||||
},
|
},
|
||||||
}: {
|
}: {
|
||||||
codeMeta?: Awaited<ReturnType<typeof getGitHubMetaForCurrentApp>>
|
codeMeta?: Awaited<ReturnType<typeof getGitHubMetaForCurrentApp>>
|
||||||
|
nextVersion: string
|
||||||
insights: ReturnType<typeof getAllInsights>
|
insights: ReturnType<typeof getAllInsights>
|
||||||
usedDeprecatedEnvVars: typeof USED_DEPRECATED_ENV_VARS
|
usedDeprecatedEnvVars: typeof USED_DEPRECATED_ENV_VARS
|
||||||
photoStats: PhotoStats
|
photoStats: PhotoStats
|
||||||
@ -276,6 +280,16 @@ export default function AdminAppInsightsClient({
|
|||||||
</span>
|
</span>
|
||||||
</a>}
|
</a>}
|
||||||
/>
|
/>
|
||||||
|
<ScoreCardRow
|
||||||
|
icon={<IconNext className="self-start translate-y-px" />}
|
||||||
|
content={<Link
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
href={`https://github.com/vercel/next.js/releases/tag/v${nextVersion}`}
|
||||||
|
target="blank"
|
||||||
|
>
|
||||||
|
Next.js {nextVersion}
|
||||||
|
</Link>}
|
||||||
|
/>
|
||||||
</ScoreCard>
|
</ScoreCard>
|
||||||
</>}
|
</>}
|
||||||
<ScoreCard title="Template recommendations">
|
<ScoreCard title="Template recommendations">
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { CategoryKey } from '../category';
|
|||||||
import {
|
import {
|
||||||
CATEGORY_VISIBILITY,
|
CATEGORY_VISIBILITY,
|
||||||
IS_BUILDING,
|
IS_BUILDING,
|
||||||
IS_PRODUCTION,
|
|
||||||
STATICALLY_OPTIMIZED_PHOTO_CATEGORIES,
|
STATICALLY_OPTIMIZED_PHOTO_CATEGORIES,
|
||||||
STATICALLY_OPTIMIZED_PHOTO_CATEGORY_OG_IMAGES,
|
STATICALLY_OPTIMIZED_PHOTO_CATEGORY_OG_IMAGES,
|
||||||
STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES,
|
STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES,
|
||||||
@ -21,8 +20,7 @@ const logStaticGenerationDetails = (count: number, content: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const staticallyGeneratePhotosIfConfigured = (type: StaticOutput) =>
|
export const staticallyGeneratePhotosIfConfigured = (type: StaticOutput) => (
|
||||||
IS_PRODUCTION && (
|
|
||||||
(type === 'page' && STATICALLY_OPTIMIZED_PHOTOS) ||
|
(type === 'page' && STATICALLY_OPTIMIZED_PHOTOS) ||
|
||||||
(type === 'image' && STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES)
|
(type === 'image' && STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES)
|
||||||
)
|
)
|
||||||
@ -47,8 +45,7 @@ export const staticallyGenerateCategoryIfConfigured = <T, K>(
|
|||||||
getData: () => Promise<T[]>,
|
getData: () => Promise<T[]>,
|
||||||
formatData: (data: T[]) => K[],
|
formatData: (data: T[]) => K[],
|
||||||
): (() => Promise<K[]>) | undefined =>
|
): (() => Promise<K[]>) | undefined =>
|
||||||
CATEGORY_VISIBILITY.includes(key) &&
|
CATEGORY_VISIBILITY.includes(key) && (
|
||||||
IS_PRODUCTION && (
|
|
||||||
(type === 'page' && STATICALLY_OPTIMIZED_PHOTO_CATEGORIES) ||
|
(type === 'page' && STATICALLY_OPTIMIZED_PHOTO_CATEGORIES) ||
|
||||||
(type === 'image' && STATICALLY_OPTIMIZED_PHOTO_CATEGORY_OG_IMAGES)
|
(type === 'image' && STATICALLY_OPTIMIZED_PHOTO_CATEGORY_OG_IMAGES)
|
||||||
)
|
)
|
||||||
|
|||||||
43
src/components/icons/IconNext.tsx
Normal file
43
src/components/icons/IconNext.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* eslint-disable max-len */
|
||||||
|
|
||||||
|
import clsx from 'clsx/lite';
|
||||||
|
|
||||||
|
export default function IconNext({
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<span className={clsx(
|
||||||
|
'text-main dark:text-black',
|
||||||
|
'border border-transparent dark:border-white/40 rounded-full',
|
||||||
|
className,
|
||||||
|
)}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180" width="1em" height="1em">
|
||||||
|
<mask height="180" id=":r8:mask0_408_134" maskUnits="userSpaceOnUse" width="180" x="0" y="0" style={{ maskType: 'alpha' }}>
|
||||||
|
<circle cx="90" cy="90" fill="black" r="90"></circle>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#:r8:mask0_408_134)">
|
||||||
|
<circle cx="90" cy="90" data-circle="true" fill="currentColor" r="90"></circle>
|
||||||
|
<path
|
||||||
|
d="M149.508 157.52L69.142 54H54V125.97H66.1136V69.3836L139.999 164.845C143.333 162.614 146.509 160.165 149.508 157.52Z"
|
||||||
|
fill="url(#:r8:paint0_linear_408_134)">
|
||||||
|
</path>
|
||||||
|
<rect fill="url(#:r8:paint1_linear_408_134)" height="72" width="12" x="115" y="54"></rect>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<linearGradient gradientUnits="userSpaceOnUse" id=":r8:paint0_linear_408_134" x1="109"
|
||||||
|
x2="144.5" y1="116.5" y2="160.5">
|
||||||
|
<stop stopColor="white"></stop>
|
||||||
|
<stop offset="1" stopColor="white" stopOpacity="0"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient gradientUnits="userSpaceOnUse" id=":r8:paint1_linear_408_134" x1="121"
|
||||||
|
x2="120.799" y1="54" y2="106.875">
|
||||||
|
<stop stopColor="white"></stop>
|
||||||
|
<stop offset="1" stopColor="white" stopOpacity="0"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -102,31 +102,31 @@ const getPhotosCacheKeys = (options: PhotoQueryOptions = {}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const revalidatePhotosKey = () =>
|
export const revalidatePhotosKey = () =>
|
||||||
revalidateTag(KEY_PHOTOS);
|
revalidateTag(KEY_PHOTOS, 'max');
|
||||||
|
|
||||||
export const revalidateAlbumsKey = () =>
|
export const revalidateAlbumsKey = () =>
|
||||||
revalidateTag(KEY_ALBUMS);
|
revalidateTag(KEY_ALBUMS, 'max');
|
||||||
|
|
||||||
export const revalidateTagsKey = () =>
|
export const revalidateTagsKey = () =>
|
||||||
revalidateTag(KEY_TAGS);
|
revalidateTag(KEY_TAGS, 'max');
|
||||||
|
|
||||||
export const revalidateRecipesKey = () =>
|
export const revalidateRecipesKey = () =>
|
||||||
revalidateTag(KEY_RECIPES);
|
revalidateTag(KEY_RECIPES, 'max');
|
||||||
|
|
||||||
export const revalidateCamerasKey = () =>
|
export const revalidateCamerasKey = () =>
|
||||||
revalidateTag(KEY_CAMERAS);
|
revalidateTag(KEY_CAMERAS, 'max');
|
||||||
|
|
||||||
export const revalidateLensesKey = () =>
|
export const revalidateLensesKey = () =>
|
||||||
revalidateTag(KEY_LENSES);
|
revalidateTag(KEY_LENSES, 'max');
|
||||||
|
|
||||||
export const revalidateFilmsKey = () =>
|
export const revalidateFilmsKey = () =>
|
||||||
revalidateTag(KEY_FILMS);
|
revalidateTag(KEY_FILMS, 'max');
|
||||||
|
|
||||||
export const revalidateFocalLengthsKey = () =>
|
export const revalidateFocalLengthsKey = () =>
|
||||||
revalidateTag(KEY_FOCAL_LENGTHS);
|
revalidateTag(KEY_FOCAL_LENGTHS, 'max');
|
||||||
|
|
||||||
export const revalidateYearsKey = () =>
|
export const revalidateYearsKey = () =>
|
||||||
revalidateTag(KEY_YEARS);
|
revalidateTag(KEY_YEARS, 'max');
|
||||||
|
|
||||||
export const revalidateAllKeys = () => {
|
export const revalidateAllKeys = () => {
|
||||||
revalidatePhotosKey();
|
revalidatePhotosKey();
|
||||||
@ -151,7 +151,7 @@ export const revalidateAllKeysAndPaths = () => {
|
|||||||
|
|
||||||
export const revalidatePhoto = (photoId: string) => {
|
export const revalidatePhoto = (photoId: string) => {
|
||||||
// Tags
|
// Tags
|
||||||
revalidateTag(photoId);
|
revalidateTag(photoId, 'max');
|
||||||
revalidateYearsKey();
|
revalidateYearsKey();
|
||||||
revalidateCamerasKey();
|
revalidateCamerasKey();
|
||||||
revalidateLensesKey();
|
revalidateLensesKey();
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "react-jsx",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user