From f4df11f68f100c65b475b21fc88d3d490191b13f Mon Sep 17 00:00:00 2001 From: Ivan Kiral Date: Tue, 27 Jan 2026 12:42:56 +0100 Subject: [PATCH 1/3] fix prerendering all of the recipes --- app.config.ts | 3 +- scripts/getPrerenderRoutes.ts | 15 ++++++++ src/utils/generateSlug.ts | 71 ++++------------------------------- src/utils/loadRecipes.ts | 38 +++++++++---------- 4 files changed, 44 insertions(+), 83 deletions(-) create mode 100644 scripts/getPrerenderRoutes.ts diff --git a/app.config.ts b/app.config.ts index ddde460..52ea252 100644 --- a/app.config.ts +++ b/app.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "@solidjs/start/config"; import { visualizer } from "rollup-plugin-visualizer"; import { VitePWA } from "vite-plugin-pwa"; +import { getPrerenderRoutes } from "./scripts/getPrerenderRoutes.ts"; export default defineConfig({ vite: { @@ -89,7 +90,7 @@ export default defineConfig({ ? `/${process.env.GITHUB_REPOSITORY.split("/")[1]}/` : "/", prerender: { - routes: ["/cookmark"], + routes: getPrerenderRoutes() as string[], }, }, }); diff --git a/scripts/getPrerenderRoutes.ts b/scripts/getPrerenderRoutes.ts new file mode 100644 index 0000000..0505a94 --- /dev/null +++ b/scripts/getPrerenderRoutes.ts @@ -0,0 +1,15 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; + +export const getPrerenderRoutes = (): ReadonlyArray => { + const dataDir = path.join(process.cwd(), "data"); + + if (!fs.existsSync(dataDir)) { + return ["/cookmark"]; + } + + const files = fs.readdirSync(dataDir).filter((f) => f.endsWith(".json")); + const recipeSlugs = files.map((file) => file.replace(".json", "")); + + return ["/cookmark", ...recipeSlugs.map((slug) => `/cookmark/recipe/${slug}`)]; +}; diff --git a/src/utils/generateSlug.ts b/src/utils/generateSlug.ts index b17673d..7b633cb 100644 --- a/src/utils/generateSlug.ts +++ b/src/utils/generateSlug.ts @@ -1,63 +1,8 @@ -const diacriticsMap = { - á: "a", - ä: "a", - č: "c", - ď: "d", - é: "e", - í: "i", - ĺ: "l", - ľ: "l", - ň: "n", - ó: "o", - ô: "o", - ŕ: "r", - š: "s", - ť: "t", - ú: "u", - ý: "y", - ž: "z", - Á: "A", - Ä: "A", - Č: "C", - Ď: "D", - É: "E", - Í: "I", - Ĺ: "L", - Ľ: "L", - Ň: "N", - Ó: "O", - Ô: "O", - Ŕ: "R", - Š: "S", - Ť: "T", - Ú: "U", - Ý: "Y", - Ž: "Z", -} as const; - -const removeDiacritics = (text: string): string => { - return text - .split("") - .map((char) => diacriticsMap[char as keyof typeof diacriticsMap] || char) - .join(""); -}; - -export const generateSlug = (text: string): string => { - return ( - removeDiacritics(text) - .toLowerCase() - .trim() - // Remove any character that is not a word character (\w = [a-zA-Z0-9_]), whitespace (\s), or hyphen (-) - // The ^ inside [] means "NOT", g means global (replace all occurrences) - .replace(/[^\w\s]/g, "") - // Replace one or more whitespace characters [\s]+ with a single underscore - // \s matches spaces, tabs, newlines; + means one or more; g means global - .replace(/[\s]+/g, "_") - // Replace one or more hyphens (-+) with a single underscore - // + means one or more consecutive hyphens; g means global - .replace(/-+/g, "_") - // Remove underscores from the beginning (^_+) or end (_+$) of the string - // ^ means start of string, $ means end of string, | means OR, + means one or more - .replace(/^_+|_+$/g, "") - ); -}; +export const generateSlug = (text: string): string => + text + .toLowerCase() + .trim() + .replace(/[^\w\s]/g, "") + .replace(/[\s]+/g, "_") + .replace(/-+/g, "_") + .replace(/^_+|_+$/g, ""); diff --git a/src/utils/loadRecipes.ts b/src/utils/loadRecipes.ts index 13e23d8..b8c205d 100644 --- a/src/utils/loadRecipes.ts +++ b/src/utils/loadRecipes.ts @@ -1,12 +1,15 @@ import type { Recipe, RecipeData } from "~/types/Recipe"; -import { generateSlug } from "./generateSlug.js"; -// Import all recipe JSON files using Vite's glob import const recipeModules = import.meta.glob("../../data/*.json", { eager: true, import: "default", }); +const extractSlugFromPath = (filePath: string): string => { + const filename = filePath.split("/").pop() || ""; + return filename.replace(".json", ""); +}; + const capitalizeFirstLetter = (str: string | null): string => { if (!str) { return "Unknown"; @@ -14,23 +17,20 @@ const capitalizeFirstLetter = (str: string | null): string => { return str.charAt(0).toUpperCase() + str.slice(1); }; -const transformRecipeData = (data: RecipeData, index: number): Recipe => { - return { - id: (index + 1).toString(), - url_slug: generateSlug(data.title), - name: data.title, - difficulty: capitalizeFirstLetter(data.difficulty) as "Easy" | "Medium" | "Hard" | "Unknown", - time: data.total_time ? `${data.total_time} min` : "N/A", - total_time: data.total_time || 0, - tags: data.tags || [], - }; -}; +const transformRecipeData = (data: RecipeData, filePath: string, index: number): Recipe => ({ + id: (index + 1).toString(), + url_slug: extractSlugFromPath(filePath), + name: data.title, + difficulty: capitalizeFirstLetter(data.difficulty) as "Easy" | "Medium" | "Hard" | "Unknown", + time: data.total_time ? `${data.total_time} min` : "N/A", + total_time: data.total_time || 0, + tags: data.tags || [], +}); -export const loadRecipes = (): Recipe[] => { - return Object.entries(recipeModules).map(([_path, data], index) => - transformRecipeData(data, index), +export const loadRecipes = (): Recipe[] => + Object.entries(recipeModules).map(([path, data], index) => + transformRecipeData(data, path, index), ); -}; export const getRecipeDataById = (id: string): RecipeData | undefined => { const recipeArray = Object.values(recipeModules); @@ -40,12 +40,12 @@ export const getRecipeDataById = (id: string): RecipeData | undefined => { export const getRecipeDataBySlug = (slug: string): RecipeData | undefined => { const recipeEntries = Object.entries(recipeModules); - const entry = recipeEntries.find(([_path, data]) => generateSlug(data.title) === slug); + const entry = recipeEntries.find(([path]) => extractSlugFromPath(path) === slug); return entry ? entry[1] : undefined; }; export const getRecipeIdBySlug = (slug: string): string | undefined => { const recipeEntries = Object.entries(recipeModules); - const index = recipeEntries.findIndex(([_path, data]) => generateSlug(data.title) === slug); + const index = recipeEntries.findIndex(([path]) => extractSlugFromPath(path) === slug); return index !== -1 ? (index + 1).toString() : undefined; }; From e8582d0fa55556618f0476f41166748f91a2fc65 Mon Sep 17 00:00:00 2001 From: Ivan Kiral Date: Tue, 27 Jan 2026 12:48:12 +0100 Subject: [PATCH 2/3] add robots.txt to prevent indexing --- public/robots.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 public/robots.txt diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / From cfd40961e10ec728a709e12c1050e6c71c2287a7 Mon Sep 17 00:00:00 2001 From: Ivan Kiral Date: Tue, 27 Jan 2026 12:53:01 +0100 Subject: [PATCH 3/3] add meal prep tag --- src/constants/filterOptions.ts | 1 + src/constants/strings.ts | 1 + src/constants/tagOptions.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/constants/filterOptions.ts b/src/constants/filterOptions.ts index aaf07e5..870ed18 100644 --- a/src/constants/filterOptions.ts +++ b/src/constants/filterOptions.ts @@ -27,6 +27,7 @@ const tagLabels: Record = { Cake: strings.tags.cake, Vegetarian: strings.tags.vegetarian, Eggs: strings.tags.eggs, + "Meal-prep": strings.tags.mealPrep, }; export const difficultyOptions = difficultyValues.map((value) => ({ diff --git a/src/constants/strings.ts b/src/constants/strings.ts index e75cd1e..e7439b3 100644 --- a/src/constants/strings.ts +++ b/src/constants/strings.ts @@ -52,6 +52,7 @@ export const strings = { cake: "Cake", vegetarian: "Vegetarian", eggs: "Eggs", + mealPrep: "Meal-prep", }, filterDrawer: { title: "Filters", diff --git a/src/constants/tagOptions.ts b/src/constants/tagOptions.ts index ebf9cfb..53e9289 100644 --- a/src/constants/tagOptions.ts +++ b/src/constants/tagOptions.ts @@ -10,6 +10,7 @@ export const tagValues = [ "Cake", "Vegetarian", "Eggs", + "Meal-prep", ] as const; export type TagValue = (typeof tagValues)[number];