refactor(about): 重构关于页面路由为静态路径

将关于页面的查询参数路由改为静态路径结构,如/about/company
更新i18n消息中的链接路径
添加新的[section]页面处理逻辑
优化静态生成参数和错误处理
This commit is contained in:
砂糖
2025-12-11 09:10:41 +08:00
parent d8ec1d4384
commit 450337a019
25 changed files with 234 additions and 215 deletions

View File

@@ -0,0 +1,120 @@
import MDXComponents from "@/components/mdx/MDXComponents";
import { Locale, LOCALES } from "@/i18n/routing";
import { constructMetadata } from "@/lib/metadata";
import fs from "fs/promises";
import { Metadata } from "next";
import { getTranslations } from "next-intl/server";
import { MDXRemote } from "next-mdx-remote-client/rsc";
import path from "path";
import remarkGfm from "remark-gfm";
// 强制静态渲染
export const dynamic = "force-static";
const options = {
parseFrontmatter: true,
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
};
// 增强添加参数校验防止undefined
async function getMDXContent(locale: string, section: string) {
// 兜底校验:确保参数是字符串
const validLocale = locale?.trim() || "en"; // 兜底为默认语言
const validSection = section?.trim() || "company"; // 兜底为默认section
// 校验LOCALES是否包含当前locale
if (!LOCALES.includes(validLocale)) {
console.error(`Locale "${validLocale}" not found in LOCALES!`);
return "";
}
const filePath = path.join(
process.cwd(),
"content",
"about",
validSection, // 使用校验后的section
`${validLocale}.mdx` // 使用校验后的locale
);
try {
// 先检查文件是否存在,避免读取不存在的文件
await fs.access(filePath);
const content = await fs.readFile(filePath, "utf-8");
return content;
} catch (error) {
console.error(`Error accessing/reading MDX file (${filePath}):`, error);
return "";
}
}
// 修正路由参数类型locale + section
type Params = {
locale: string;
section: string;
};
type MetadataProps = {
params: Params;
};
export async function generateMetadata({
params,
}: MetadataProps): Promise<Metadata> {
const { locale, section } = params;
const t = await getTranslations({ locale, namespace: "About" });
return constructMetadata({
page: `About - ${section}`,
title: t(`title`) || t("title"), // 适配不同section的标题
description: t(`description`) || t("description"),
locale: locale as Locale,
path: `/about/${section}`,
canonicalUrl: `/about/${section}`,
});
}
// 页面组件从params获取locale和section路由参数
export default async function AboutPage({
params,
}: {
params: Promise<Params>;
}) {
const { locale, section } = await params;
console.log(`Rendering AboutPage for locale: ${locale}, section: ${section}`);
// 调用带校验的getContent函数
const content = await getMDXContent(locale, section);
return (
<article className="w-full md:w-3/5 px-2 md:px-12">
<MDXRemote
source={content}
components={MDXComponents}
options={options}
/>
</article>
);
}
// 关键生成locale + section的所有静态组合覆盖vi/organization等
export async function generateStaticParams() {
const sections = ['company', 'awards', 'base', 'culture', 'history', 'organization'];
// 确保LOCALES包含"vi"越南语否则会生成undefined
if (!LOCALES.includes("vi")) {
console.warn("⚠️ LOCALES does not include 'vi'! Add it to fix /vi/about/* paths.");
// 临时兜底如果没有vi手动添加或检查你的i18n/routing.ts
// LOCALES.push("vi");
}
// 生成所有locale × section的组合
return LOCALES.flatMap((locale) =>
sections.map((section) => ({
locale,
section,
}))
);
}

View File

@@ -1,88 +1,32 @@
import MDXComponents from "@/components/mdx/MDXComponents"; import { LOCALES } from "@/i18n/routing";
import { Locale, LOCALES } from "@/i18n/routing"; import { redirect } from "next/navigation";
import { constructMetadata } from "@/lib/metadata";
import fs from "fs/promises";
import { Metadata } from "next";
import { getTranslations } from "next-intl/server";
import { MDXRemote } from "next-mdx-remote-client/rsc";
import path from "path";
import remarkGfm from "remark-gfm";
const options = { // 重定向到 /about/company
parseFrontmatter: true, export default async function Page({
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
};
async function getMDXContent(locale: string, section: string) {
const filePath = path.join(
process.cwd(),
"content",
"about",
section,
`${locale}.mdx`
);
try {
const content = await fs.readFile(filePath, "utf-8");
return content;
} catch (error) {
console.error(`Error reading MDX file: ${error}`);
return "";
}
}
type Params = Promise<{
locale: string;
}>;
type MetadataProps = {
params: Params;
};
export async function generateMetadata({
params, params,
}: MetadataProps): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "About" });
return constructMetadata({
page: "About",
title: t("title"),
description: t("description"),
locale: locale as Locale,
path: `/about`,
canonicalUrl: `/about`,
});
}
export default async function AboutPage({
params,
searchParams
}: { }: {
params: Params; params: Promise<{ locale: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) { }) {
const { locale } = await params; const { locale } = await params;
const resolvedSearchParams = await searchParams; return redirect(`/${locale}/about/company`);
const section = (resolvedSearchParams.section as string) || "company"; }
const content = await getMDXContent(locale, section); // 关键生成locale + section的所有静态组合覆盖vi/organization等
export async function generateStaticParams() {
const sections = ['company', 'awards', 'base', 'culture', 'history', 'organization'];
return ( // 确保LOCALES包含"vi"越南语否则会生成undefined
<article className="w-full md:w-3/5 px-2 md:px-12"> if (!LOCALES.includes("vi")) {
<MDXRemote console.warn("⚠️ LOCALES does not include 'vi'! Add it to fix /vi/about/* paths.");
source={content} // 临时兜底如果没有vi手动添加或检查你的i18n/routing.ts
components={MDXComponents} // LOCALES.push("vi");
options={options} }
/>
</article> // 生成所有locale × section的组合
return LOCALES.flatMap((locale) =>
sections.map((section) => ({
locale,
section,
}))
); );
} }
export async function generateStaticParams() {
return LOCALES.map((locale) => ({
locale,
}));
}

View File

@@ -72,51 +72,6 @@ async function getMDXContent(locale: string, section: string): Promise<BlogPost>
} }
} }
/**
* 解析MDX内容提取frontmatter和正文
*/
function parseMDXContent(content: string): {
frontmatter: Record<string, any>
content: string
} {
// 匹配frontmatter的正则表达式---开头和结尾)
const frontmatterRegex = /^---\s*[\r\n]([\s\S]*?)[\r\n]---\s*[\r\n]/;
const match = frontmatterRegex.exec(content);
let frontmatter: Record<string, any> = {};
let postContent = content;
if (match && match[1]) {
// 提取frontmatter部分并解析
const frontmatterStr = match[1];
postContent = content.slice(match[0].length);
// 解析frontmatter的每一行
const lines = frontmatterStr.split(/[\r\n]+/);
lines.forEach(line => {
// 跳过空行和注释
if (!line.trim() || line.trim().startsWith('#')) return;
// 分割键值对(支持值中有冒号的情况)
const colonIndex = line.indexOf(':');
if (colonIndex === -1) return;
const key = line.substring(0, colonIndex).trim();
let value = line.substring(colonIndex + 1).trim();
// 移除值的引号(如果有)
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
value = value.substring(1, value.length - 1);
}
frontmatter[key] = value;
});
}
return { frontmatter, content: postContent };
}
export async function generateMetadata({ export async function generateMetadata({
params, params,

View File

@@ -9,8 +9,6 @@ import { getTranslations } from "next-intl/server";
type Params = Promise<{ locale: string }>; type Params = Promise<{ locale: string }>;
type SearchParams = { page?: string; category?: string };
type MetadataProps = { type MetadataProps = {
params: Params; params: Params;
}; };
@@ -33,21 +31,21 @@ export async function generateMetadata({
export default async function Page({ export default async function Page({
params, params,
searchParams, // searchParams,
}: { }: {
params: Params; params: Params;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) { }) {
const { locale } = await params; const { locale } = await params;
const resolvedSearchParams = await searchParams; // const resolvedSearchParams = await searchParams;
const category = resolvedSearchParams.category as string || ""; // const category = resolvedSearchParams.category as string || "";
let { posts } = await getPosts(locale); let { posts } = await getPosts(locale);
const t = await getTranslations("Blog"); const t = await getTranslations("Blog");
const pageRaw = resolvedSearchParams.page as string || "1"; // const pageRaw = resolvedSearchParams.page as string || "1";
const page = Math.max(1, parseInt(pageRaw, 10)); // const page = Math.max(1, parseInt(10, 10));
const page = 1;
const pageSize = 10; const pageSize = 10;
const totalPages = Math.max(1, Math.ceil(posts.length / pageSize)); const totalPages = Math.max(1, Math.ceil(posts.length / pageSize));
const start = (page - 1) * pageSize; const start = (page - 1) * pageSize;

View File

@@ -1,16 +1,16 @@
import HomeComponent from "@/components/home"; import HomeComponent from "@/components/home";
// export const dynamic = "force-static"; export const dynamic = "force-static";
export default function Home() { export default function Home() {
return <HomeComponent />; return <HomeComponent />;
} }
// export async function generateStaticParams() { export async function generateStaticParams() {
// return [ return [
// { locale: 'en' }, { locale: 'en' },
// { locale: 'zh' }, { locale: 'zh' },
// { locale: 'vi' }, { locale: 'vi' },
// // { locale: 'ja' }, // { locale: 'ja' },
// ] ]
// } }

View File

@@ -169,12 +169,12 @@ export async function generateStaticParams() {
try { try {
const defaultLocale = LOCALES[0]; const defaultLocale = LOCALES[0];
const { products }: { products: Product[] } = await getProducts(defaultLocale); const { products }: { products: Product[] } = await getProducts(defaultLocale);
const validProducts = products.filter((product) => product.slug && product.title); const validProducts = products.filter((product) => product.slug.toString() && product.title);
return LOCALES.flatMap((locale) => return LOCALES.flatMap((locale) =>
validProducts.map((product) => ({ validProducts.map((product) => ({
locale, locale,
slug: product.slug, slug: product.slug.toString(),
})) }))
); );
} catch (error) { } catch (error) {

View File

@@ -3,7 +3,7 @@ import type { MetadataRoute } from 'next'
const siteUrl = siteConfig.url const siteUrl = siteConfig.url
// export const dynamic = "force-static" export const dynamic = "force-static"
export default function robots(): MetadataRoute.Robots { export default function robots(): MetadataRoute.Robots {
return { return {

View File

@@ -7,7 +7,7 @@ const siteUrl = siteConfig.url
type ChangeFrequency = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never' | undefined type ChangeFrequency = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never' | undefined
// export const dynamic = "force-static" export const dynamic = "force-static"
export default async function sitemap(): Promise<MetadataRoute.Sitemap> { export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
// Static pages // Static pages

View File

@@ -35,7 +35,7 @@ export default function ProductTabsClient({ detail, spec, packaging, locale }: P
className={`px-4 py-2 transition-colors ${activeTab === 'packaging' ? 'bg-orange-50 text-orange-600 font-medium' : 'hover:bg-gray-50'}`} className={`px-4 py-2 transition-colors ${activeTab === 'packaging' ? 'bg-orange-50 text-orange-600 font-medium' : 'hover:bg-gray-50'}`}
onClick={() => setActiveTab('packaging')} onClick={() => setActiveTab('packaging')}
> >
{t('productPackaging')} {t('productPacking')}
</button> </button>
</div> </div>
{/* 内容区 */} {/* 内容区 */}

View File

@@ -4,7 +4,6 @@ import LocaleSwitcher from "@/components/LocaleSwitcher";
import { siteConfig } from "@/config/site"; import { siteConfig } from "@/config/site";
import { Link as I18nLink } from "@/i18n/routing"; import { Link as I18nLink } from "@/i18n/routing";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import Image from "next/image";
const Header = () => { const Header = () => {
const t = useTranslations("Home"); const t = useTranslations("Home");
@@ -18,7 +17,7 @@ const Header = () => {
prefetch={false} prefetch={false}
className="flex items-center space-x-1 font-bold" className="flex items-center space-x-1 font-bold"
> >
<Image <img
alt={siteConfig.name} alt={siteConfig.name}
src="/logo.png" src="/logo.png"
className="w-6 h-6" className="w-6 h-6"

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251102082125.jpg - http://kelunpuzhonggong.com/upload/20251102082125.jpg
detail: Textured/Non-textured detail: Textured/Non-textured
spec: spec:
- Nominal thickness: 0.6-6.0 - 'Nominal thickness: 0.6-6.0'
- Nominal width: 800-1250 - 'Nominal width: 800-1250'
- Inner diameter of steel coil: 610 - 'Inner diameter of steel coil: 610'
packaging: Single coil packaging: Single coil
slug: 1 slug: 1
--- ---

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251021161717.jpg - http://kelunpuzhonggong.com/upload/20251021161717.jpg
detail: detail:
spec: spec:
- Nominal thickness: 0.16-3.0 - 'Nominal thickness: 0.16-3.0'
- Nominal width: 800-1250 - 'Nominal width: 800-1250'
- Inner diameter of steel coil: 610 - 'Inner diameter of steel coil: 610'
packaging: packaging:
slug: 2 slug: 2
--- ---

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251021161717.jpg - http://kelunpuzhonggong.com/upload/20251021161717.jpg
detail: detail:
spec: spec:
- Nominal thickness: 0.16-3.0 - 'Nominal thickness: 0.16-3.0'
- Nominal width: 800-1250 - 'Nominal width: 800-1250'
- Inner diameter of steel coil: 610 - 'Inner diameter of steel coil: 610'
packaging: packaging:
slug: 3 slug: 3
--- ---

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251102082125.jpg - http://kelunpuzhonggong.com/upload/20251102082125.jpg
detail: Có kết cấu/Không kết cấu detail: Có kết cấu/Không kết cấu
spec: spec:
- Độ dày danh nghĩa: 0.6-6.0 - 'Độ dày danh nghĩa: 0.6-6.0'
- Chiều rộng danh nghĩa: 800-1250 - 'Chiều rộng danh nghĩa: 800-1250'
- Đường kính trong cuộn thép: 610 - 'Đường kính trong cuộn thép: 610'
packaging: Cuộn đơn packaging: Cuộn đơn
slug: 1 slug: 1
--- ---

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251021161717.jpg - http://kelunpuzhonggong.com/upload/20251021161717.jpg
detail: detail:
spec: spec:
- Độ dày danh nghĩa: 0.16-3.0 - 'Độ dày danh nghĩa: 0.16-3.0'
- Chiều rộng danh nghĩa: 800-1250 - 'Chiều rộng danh nghĩa: 800-1250'
- Đường kính trong cuộn thép: 610 - 'Đường kính trong cuộn thép: 610'
packaging: packaging:
slug: 2 slug: 2
--- ---

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251021161717.jpg - http://kelunpuzhonggong.com/upload/20251021161717.jpg
detail: detail:
spec: spec:
- Độ dày danh nghĩa: 0.16-3.0 - 'Độ dày danh nghĩa: 0.16-3.0'
- Chiều rộng danh nghĩa: 800-1250 - 'Chiều rộng danh nghĩa: 800-1250'
- Đường kính trong cuộn thép: 610 - 'Đường kính trong cuộn thép: 610'
packaging: packaging:
slug: 3 slug: 3
--- ---

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251102082125.jpg - http://kelunpuzhonggong.com/upload/20251102082125.jpg
detail: 毛化/无毛化 detail: 毛化/无毛化
spec: spec:
- 公称厚度0.6-6.0 - '公称厚度0.6-6.0'
- 公称宽度800-1250 - '公称宽度800-1250'
- 钢卷内径610 - '钢卷内径610'
packaging: 单卷 packaging: 单卷
slug: 1 slug: 1
--- ---

View File

@@ -7,9 +7,9 @@ images:
- http://kelunpuzhonggong.com/upload/20251021161717.jpg - http://kelunpuzhonggong.com/upload/20251021161717.jpg
detail: detail:
spec: spec:
- 公称厚度0.16-3.0 - '公称厚度0.16-3.0'
- 公称宽度800-1250 - '公称宽度800-1250'
- 钢卷内径610 - '钢卷内径610'
packaging: packaging:
slug: 2 slug: 2
--- ---

View File

@@ -8,8 +8,8 @@ images:
detail: detail:
spec: spec:
- 公称厚度0.16-3.0 - 公称厚度0.16-3.0
- 公称宽度800-1250 - '公称宽度800-1250'
- 钢卷内径610 - '钢卷内径610'
packaging: packaging:
slug: 3 slug: 3
--- ---

View File

@@ -17,27 +17,27 @@
"children": [ "children": [
{ {
"name": "Company Profile", "name": "Company Profile",
"href": "/about?section=company" "href": "/about/company"
}, },
{ {
"name": "Corporate Culture", "name": "Corporate Culture",
"href": "/about?section=culture" "href": "/about/culture"
}, },
{ {
"name": "Production Base", "name": "Production Base",
"href": "/about?section=base" "href": "/about/base"
}, },
{ {
"name": "Organizational Structure", "name": "Organizational Structure",
"href": "/about?section=organization" "href": "/about/organization"
}, },
{ {
"name": "Honors and Qualifications", "name": "Honors and Qualifications",
"href": "/about?section=awards" "href": "/about/awards"
}, },
{ {
"name": "Development History", "name": "Development History",
"href": "/about?section=history" "href": "/about/history"
} }
] ]
}, },
@@ -75,32 +75,32 @@
"title": "About Us", "title": "About Us",
"links": [ "links": [
{ {
"href": "/en/about?section=company", "href": "/en/about/company",
"name": "Company Profile", "name": "Company Profile",
"useA": true "useA": true
}, },
{ {
"href": "/en/about?section=culture", "href": "/en/about/culture",
"name": "Corporate Culture", "name": "Corporate Culture",
"useA": true "useA": true
}, },
{ {
"href": "/en/about?section=base", "href": "/en/about/base",
"name": "Production Base", "name": "Production Base",
"useA": true "useA": true
}, },
{ {
"href": "/en/about?section=organization", "href": "/en/about/organization",
"name": "Organizational Structure", "name": "Organizational Structure",
"useA": true "useA": true
}, },
{ {
"href": "/en/about?section=awards", "href": "/en/about/awards",
"name": "Honors and Qualifications", "name": "Honors and Qualifications",
"useA": true "useA": true
}, },
{ {
"href": "/en/about?section=history", "href": "/en/about/history",
"name": "Development History", "name": "Development History",
"useA": true "useA": true
} }

View File

@@ -17,27 +17,27 @@
"children": [ "children": [
{ {
"name": "Tổng quan công ty", "name": "Tổng quan công ty",
"href": "/about?section=company" "href": "/about/company"
}, },
{ {
"name": "Văn hóa doanh nghiệp", "name": "Văn hóa doanh nghiệp",
"href": "/about?section=culture" "href": "/about/culture"
}, },
{ {
"name": "Cơ sở sản xuất", "name": "Cơ sở sản xuất",
"href": "/about?section=base" "href": "/about/base"
}, },
{ {
"name": "Cơ cấu tổ chức", "name": "Cơ cấu tổ chức",
"href": "/about?section=organization" "href": "/about/organization"
}, },
{ {
"name": "Danh hiệu và chứng chỉ", "name": "Danh hiệu và chứng chỉ",
"href": "/about?section=awards" "href": "/about/awards"
}, },
{ {
"name": "Lịch sử phát triển", "name": "Lịch sử phát triển",
"href": "/about?section=history" "href": "/about/history"
} }
] ]
}, },
@@ -75,32 +75,32 @@
"title": "Về chúng tôi", "title": "Về chúng tôi",
"links": [ "links": [
{ {
"href": "/vi/about?section=company", "href": "/vi/about/company",
"name": "Tổng quan công ty", "name": "Tổng quan công ty",
"useA": true "useA": true
}, },
{ {
"href": "/vi/about?section=culture", "href": "/vi/about/culture",
"name": "Văn hóa doanh nghiệp", "name": "Văn hóa doanh nghiệp",
"useA": true "useA": true
}, },
{ {
"href": "/vi/about?section=base", "href": "/vi/about/base",
"name": "Cơ sở sản xuất", "name": "Cơ sở sản xuất",
"useA": true "useA": true
}, },
{ {
"href": "/vi/about?section=organization", "href": "/vi/about/organization",
"name": "Cơ cấu tổ chức", "name": "Cơ cấu tổ chức",
"useA": true "useA": true
}, },
{ {
"href": "/vi/about?section=awards", "href": "/vi/about/awards",
"name": "Danh hiệu và chứng chỉ", "name": "Danh hiệu và chứng chỉ",
"useA": true "useA": true
}, },
{ {
"href": "/vi/about?section=history", "href": "/vi/about/history",
"name": "Lịch sử phát triển", "name": "Lịch sử phát triển",
"useA": true "useA": true
} }

View File

@@ -17,27 +17,27 @@
"children": [ "children": [
{ {
"name": "公司简介", "name": "公司简介",
"href": "/about?section=company" "href": "/about/company"
}, },
{ {
"name": "企业文化", "name": "企业文化",
"href": "/about?section=culture" "href": "/about/culture"
}, },
{ {
"name": "生产基地", "name": "生产基地",
"href": "/about?section=base" "href": "/about/base"
}, },
{ {
"name": "组织架构", "name": "组织架构",
"href": "/about?section=organization" "href": "/about/organization"
}, },
{ {
"name": "荣誉资质", "name": "荣誉资质",
"href": "/about?section=awards" "href": "/about/awards"
}, },
{ {
"name": "发展历程", "name": "发展历程",
"href": "/about?section=history" "href": "/about/history"
} }
] ]
}, },
@@ -75,32 +75,32 @@
"title": "关于我们", "title": "关于我们",
"links": [ "links": [
{ {
"href": "/zh/about?section=company", "href": "/zh/about/company",
"name": "公司简介", "name": "公司简介",
"useA": true "useA": true
}, },
{ {
"href": "/zh/about?section=culture", "href": "/zh/about/culture",
"name": "企业文化", "name": "企业文化",
"useA": true "useA": true
}, },
{ {
"href": "/zh/about?section=base", "href": "/zh/about/base",
"name": "生产基地", "name": "生产基地",
"useA": true "useA": true
}, },
{ {
"href": "/zh/about?section=organization", "href": "/zh/about/organization",
"name": "组织架构", "name": "组织架构",
"useA": true "useA": true
}, },
{ {
"href": "/zh/about?section=awards", "href": "/zh/about/awards",
"name": "荣誉资质", "name": "荣誉资质",
"useA": true "useA": true
}, },
{ {
"href": "/zh/about?section=history", "href": "/zh/about/history",
"name": "发展历程", "name": "发展历程",
"useA": true "useA": true
} }

View File

@@ -4,7 +4,8 @@ const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
output: "standalone", output: "export",
// output: "standalone",
images: { images: {
remotePatterns: [ remotePatterns: [
...(process.env.R2_PUBLIC_URL ...(process.env.R2_PUBLIC_URL

7
package-lock.json generated
View File

@@ -45,6 +45,7 @@
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"baseline-browser-mapping": "^2.9.5",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.0.0", "eslint-config-next": "16.0.0",
"postcss": "^8.4.38", "postcss": "^8.4.38",
@@ -2666,9 +2667,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.8.30", "version": "2.9.5",
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz", "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz",
"integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==", "integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {

View File

@@ -49,6 +49,7 @@
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"baseline-browser-mapping": "^2.9.5",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.0.0", "eslint-config-next": "16.0.0",
"postcss": "^8.4.38", "postcss": "^8.4.38",