117 lines
2.7 KiB
JavaScript
117 lines
2.7 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const repoRoot = path.resolve(__dirname, "..", "..");
|
|
const mealsPath = path.join(repoRoot, "data", "meals.json");
|
|
|
|
function validateThumbnail(meal, index) {
|
|
if (meal.thumbnail === undefined) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
!meal.thumbnail ||
|
|
typeof meal.thumbnail !== "object" ||
|
|
Array.isArray(meal.thumbnail)
|
|
) {
|
|
throw new Error(`Meal ${index} has an invalid "thumbnail" object`);
|
|
}
|
|
|
|
if (meal.thumbnail.focus === undefined) {
|
|
return;
|
|
}
|
|
|
|
const { focus } = meal.thumbnail;
|
|
|
|
if (!focus || typeof focus !== "object" || Array.isArray(focus)) {
|
|
throw new Error(`Meal ${index} has an invalid "thumbnail.focus" object`);
|
|
}
|
|
|
|
for (const axis of ["x", "y"]) {
|
|
if (typeof focus[axis] !== "number" || !Number.isFinite(focus[axis])) {
|
|
throw new Error(
|
|
`Meal ${index} has a non-numeric thumbnail focus value for "${axis}"`
|
|
);
|
|
}
|
|
|
|
if (focus[axis] < 0 || focus[axis] > 1) {
|
|
throw new Error(
|
|
`Meal ${index} thumbnail focus "${axis}" must be between 0 and 1`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function validateMeals(meals) {
|
|
if (!Array.isArray(meals)) {
|
|
throw new Error("data/meals.json must contain an array");
|
|
}
|
|
|
|
const ids = new Set();
|
|
|
|
for (const [index, meal] of meals.entries()) {
|
|
if (!meal || typeof meal !== "object") {
|
|
throw new Error(`Meal at index ${index} must be an object`);
|
|
}
|
|
|
|
for (const field of ["id", "title", "description"]) {
|
|
if (typeof meal[field] !== "string" || meal[field].length === 0) {
|
|
throw new Error(`Meal ${index} is missing required string field "${field}"`);
|
|
}
|
|
}
|
|
|
|
if (!/^\d+$/.test(meal.id)) {
|
|
throw new Error(`Meal ${index} has a non-numeric id "${meal.id}"`);
|
|
}
|
|
|
|
if (meal.position !== undefined && typeof meal.position !== "string") {
|
|
throw new Error(`Meal ${index} has a non-string "position" value`);
|
|
}
|
|
|
|
if (ids.has(meal.id)) {
|
|
throw new Error(`Duplicate meal id "${meal.id}" found in data/meals.json`);
|
|
}
|
|
|
|
ids.add(meal.id);
|
|
|
|
validateThumbnail(meal, index);
|
|
}
|
|
}
|
|
|
|
function loadMeals() {
|
|
const meals = JSON.parse(fs.readFileSync(mealsPath, "utf8"));
|
|
|
|
validateMeals(meals);
|
|
|
|
return meals;
|
|
}
|
|
|
|
function saveMeals(meals) {
|
|
validateMeals(meals);
|
|
fs.writeFileSync(mealsPath, `${JSON.stringify(meals, null, 2)}\n`);
|
|
}
|
|
|
|
function getNextMealId(meals) {
|
|
if (meals.length === 0) {
|
|
return "01";
|
|
}
|
|
|
|
const nextNumber =
|
|
Math.max(...meals.map((meal) => Number.parseInt(meal.id, 10))) + 1;
|
|
const idWidth = Math.max(
|
|
2,
|
|
...meals.map((meal) => meal.id.length),
|
|
String(nextNumber).length
|
|
);
|
|
|
|
return String(nextNumber).padStart(idWidth, "0");
|
|
}
|
|
|
|
module.exports = {
|
|
getNextMealId,
|
|
loadMeals,
|
|
mealsPath,
|
|
repoRoot,
|
|
saveMeals,
|
|
};
|