diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json
index 76591198d..189a2d501 100644
--- a/apps/nextjs/package.json
+++ b/apps/nextjs/package.json
@@ -47,6 +47,7 @@
"chroma-js": "^2.4.2",
"dayjs": "^1.11.10",
"dotenv": "^16.4.5",
+ "glob": "^10.3.12",
"jotai": "^2.8.0",
"next": "^14.2.3",
"postcss-preset-mantine": "^1.15.0",
@@ -60,10 +61,10 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
+ "@types/chroma-js": "2.4.4",
"@types/node": "^20.12.7",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
- "@types/chroma-js": "2.4.4",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"prettier": "^3.2.5",
diff --git a/apps/nextjs/src/app/[locale]/manage/about/page.tsx b/apps/nextjs/src/app/[locale]/manage/about/page.tsx
index b7ff7b2a7..450995a74 100644
--- a/apps/nextjs/src/app/[locale]/manage/about/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/about/page.tsx
@@ -13,10 +13,11 @@ import {
Title,
} from "@mantine/core";
import { IconLanguage, IconLibrary, IconUsers } from "@tabler/icons-react";
+import { setStaticParamsLocale } from "next-international/server";
-import { getScopedI18n } from "@homarr/translation/server";
+import { getScopedI18n, getStaticParams } from "@homarr/translation/server";
-import { getPackageAttributes } from "~/versions/package-reader";
+import { getPackageAttributesAsync } from "~/versions/package-reader";
import logo from "../../../../../public/logo/logo.png";
import classes from "./accordion.module.css";
@@ -29,9 +30,16 @@ export async function generateMetadata() {
};
}
-export default async function AboutPage() {
+interface PageProps {
+ params: {
+ locale: string;
+ };
+}
+
+export default async function AboutPage({ params: { locale } }: PageProps) {
+ setStaticParamsLocale(locale);
const t = await getScopedI18n("management.page.about");
- const attributes = getPackageAttributes();
+ const attributes = await getPackageAttributesAsync();
return (
@@ -75,15 +83,17 @@ export default async function AboutPage() {
- {Object.entries(attributes.dependencies).map(([key, value]) => (
-
- {value.includes("workspace:") ? (
- {key}
- ) : (
- {key}
- )}
-
- ))}
+ {Object.entries(attributes.dependencies)
+ .sort(([key1], [key2]) => key1.localeCompare(key2))
+ .map(([key, value]) => (
+
+ {value.includes("workspace:") ? (
+ {key}
+ ) : (
+ {key}
+ )}
+
+ ))}
@@ -91,3 +101,9 @@ export default async function AboutPage() {
);
}
+
+export function generateStaticParams() {
+ return getStaticParams();
+}
+
+export const dynamic = "force-static";
diff --git a/apps/nextjs/src/versions/package-reader.ts b/apps/nextjs/src/versions/package-reader.ts
index 4142309ee..0862c711c 100644
--- a/apps/nextjs/src/versions/package-reader.ts
+++ b/apps/nextjs/src/versions/package-reader.ts
@@ -1,13 +1,40 @@
+import fsPromises from "fs/promises";
+import { glob } from "glob";
+
import packageJson from "~/../package.json";
const getPackageVersion = () => packageJson.version;
-const getDependencies = (): PackageJsonDependencies => packageJson.dependencies;
+const getDependenciesAsync = async (): Promise => {
+ const pathNames = await glob("**/package.json", {
+ ignore: "node_modules/**",
+ cwd: "../../",
+ absolute: true,
+ });
+ const packageContents = await Promise.all(
+ pathNames.map(async (path) => await fsPromises.readFile(path, "utf-8")),
+ );
+ const packageDependencies = packageContents
+ .map(
+ (packageContent) =>
+ (JSON.parse(packageContent) as PackageJson).dependencies,
+ )
+ .filter((dependencies) => dependencies !== undefined);
-export const getPackageAttributes = () => {
+ let dependencies = {};
+ for (const dependenciesOfPackage of packageDependencies) {
+ dependencies = { ...dependencies, ...dependenciesOfPackage };
+ }
+ return dependencies;
+};
+
+export const getPackageAttributesAsync = async () => {
return {
version: getPackageVersion(),
- dependencies: getDependencies(),
+ dependencies: await getDependenciesAsync(),
};
};
type PackageJsonDependencies = { [key in string]: string };
+interface PackageJson {
+ dependencies: PackageJsonDependencies | undefined;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4974982cb..97db7aba3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -159,6 +159,9 @@ importers:
dotenv:
specifier: ^16.4.5
version: 16.4.5
+ glob:
+ specifier: ^10.3.12
+ version: 10.3.12
jotai:
specifier: ^2.8.0
version: 2.8.0(@types/react@18.3.1)(react@18.3.1)
@@ -3521,6 +3524,11 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
+ glob@10.3.12:
+ resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
@@ -4499,6 +4507,10 @@ packages:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'}
+ path-scurry@1.10.2:
+ resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
@@ -8432,6 +8444,14 @@ snapshots:
minipass: 7.0.4
path-scurry: 1.10.1
+ glob@10.3.12:
+ dependencies:
+ foreground-child: 3.1.1
+ jackspeak: 2.3.6
+ minimatch: 9.0.4
+ minipass: 7.0.4
+ path-scurry: 1.10.2
+
glob@7.2.3:
dependencies:
fs.realpath: 1.0.0
@@ -9491,6 +9511,11 @@ snapshots:
lru-cache: 10.2.0
minipass: 7.0.4
+ path-scurry@1.10.2:
+ dependencies:
+ lru-cache: 10.2.0
+ minipass: 7.0.4
+
path-type@4.0.0: {}
pathe@1.1.2: {}