Skip to content

Revi performance - #1

Open
danielPalmera wants to merge 184 commits into
biko2:mainfrom
danielPalmera:revi_performance
Open

Revi performance#1
danielPalmera wants to merge 184 commits into
biko2:mainfrom
danielPalmera:revi_performance

Conversation

@danielPalmera

@danielPalmera danielPalmera commented Jun 15, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Launched Astro-based frontend for The Artemis diary with post listing and individual post pages
    • Added responsive post card components with related articles suggestions
    • Implemented site header, footer, and hero section layouts
  • Documentation

    • Added frontend README with setup and deployment instructions
    • Created hexagonal architecture guide for frontend code organization
  • Chores

    • Configured ESLint, Prettier, and TypeScript for frontend development
    • Set up Tailwind CSS with custom theme and utilities
    • Added VS Code extensions and launch configurations
    • Updated Husky pre-commit hooks for automated linting and testing
  • Tests

    • Added unit tests for date formatting and related articles filtering

danielPalmera and others added 30 commits May 28, 2026 14:54
- Created index.astro for the main page displaying a grid of posts fetched from the API.
- Implemented post detail page [slug].astro with SEO metadata and dynamic content rendering.
- Added TypeScript configuration file tsconfig.json for strict type checking.
- Created Header component for site branding with logo.
- Implemented Footer component with a thank you message and link.
- Defined Post type in TypeScript for better type safety in blog posts.
…elated posts

refactor: remove fixed positioning from footer
- Replaced Header and Footer components with a BaseLayout component in the post page.
- Updated SEO metadata handling in BaseLayout for better organization and reusability.
- Adjusted global CSS variables for background color.
- Added new BaseLayout component to manage common layout structure and SEO tags.
- Enhanced the fetching of posts in BaseLayout for better data management.
- Improved styling for the main content area and grid layout for posts.
danielPalmera and others added 28 commits June 10, 2026 12:35
…d PostCard.astro for consistency

fix: update link in 404.astro to use base path for home navigation
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR bootstraps a complete Astro frontend for "The Artemis diary" blog. It converts the frontend/ directory from a bare serve-based placeholder to a fully configured Astro + Tailwind + TypeScript project, introducing typed data contracts, URL/date/image/post utilities with Vitest tests, an atomic component hierarchy, a BaseLayout with OG/Twitter metadata, three pages (home, post detail, 404), and project tooling (ESLint, Prettier, Husky pre-commit hooks).

Changes

Astro frontend bootstrap

Layer / File(s) Summary
Project scaffold, dependencies, and tooling
frontend/package.json, frontend/tsconfig.json, frontend/astro.config.mjs, frontend/eslint.config.js, frontend/.prettierrc, package.json, .husky/pre-commit, pnpm-workspace.yaml, .gitignore, frontend/.gitignore, .vscode/settings.json, frontend/.vscode/*
Converts frontend/ to an Astro ESM project with Tailwind, Vitest, ESLint (TypeScript+Astro parsers, filename-casing rules), Prettier, and Husky pre-commit linting/testing. Root scripts add build:api to preview and wire husky prepare.
Domain types and global styles
frontend/src/types/cover-image.ts, frontend/src/types/img-props.ts, frontend/src/types/post.ts, frontend/src/styles/global.css, frontend/index.html
Defines CoverImage, ImgProps, and Post TypeScript interfaces. Adds global.css with Tailwind import, CSS custom properties, @theme font, and @utility typography helpers. Removes the old index.html placeholder.
Core utilities with tests and mock data
frontend/src/utils/base.ts, frontend/src/utils/date.ts, frontend/src/utils/date.test.ts, frontend/src/utils/images.ts, frontend/src/utils/posts.ts, frontend/src/utils/posts.test.ts, frontend/src/mocks/mock-posts-data.ts
Adds base (normalized BASE_URL), formatDate (locale formatter with invalid-date guard), localImg/absoluteImg (base-prefixed and site-absolute image URL helpers), getPosts (API fetch), and getRelatedPostByFirstTag (first-tag filter). Vitest suites and mock Post[] data cover happy and error paths.
Atom and molecule UI components
frontend/src/components/atoms/*, frontend/src/components/molecules/PostCard.astro, frontend/src/components/molecules/RelatedArticlesList.astro
Adds H1, H3, PreTitle, Img, DatePublished atoms and PostCard (clickable post card with conditional excerpt) and RelatedArticlesList (related post grid via getRelatedPostByFirstTag) molecules.
Organism components and BaseLayout
frontend/src/components/organisms/*, frontend/src/layouts/BaseLayout.astro
Adds Header (logo link), Footer (copyright), Hero (mission intro), GridPost (full post grid), HeaderPost (cover image + title + date + reading time), and RelatedArticles organisms. BaseLayout wires ClientRouter, typed OG/Twitter meta tags, a head slot, and Header/Footer composition.
Pages: index, post detail, and 404
frontend/src/pages/index.astro, frontend/src/pages/posts/[slug].astro, frontend/src/pages/404.astro
index.astro renders Hero + GridPost with OG metadata. [slug].astro defines getStaticPaths fetching all posts from PUBLIC_API_BASE, renders HeaderPost + article content + RelatedArticles with per-post article meta tags. 404.astro renders a not-found page with a home link.
Documentation and architecture notes
README.md, frontend/README.md, frontend/arquitectura.md
Adds root README.md with repo directory tree, frontend README with Astro setup and deployment notes, and arquitectura.md proposing a hexagonal architecture with layer import rules and an ArticleRepository port/adapter example.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 A blank page once said "Hello Moon!"
Now Artemis rockets soar under CSS runes,
With atoms and organisms all snapped into place,
A getStaticPaths launched into infinite space,
The diary is live — posts fetched, slugs resolved,
Every formatDate tested, every lint rule enforced!
Hippity-hop, the frontend's been born. 🚀

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Revi performance' is vague and unclear. 'Revi' appears to be an abbreviation or typo, and it does not clearly describe the changeset which establishes a complete frontend Astro project with components, utilities, configuration, and project structure. Clarify the title to reflect the main purpose: either explain what 'Revi' means, or replace it with a clear description like 'Set up Artemis blog frontend with Astro' or 'Initialize frontend project structure and components'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (11)
README.md-4-72 (1)

4-72: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Refresh the repository tree.

This block still shows stale paths (formatDate.js, postUtils.js, postUtilss.ts) and misses new files like frontend/src/pages/404.astro, so it no longer reflects the frontend layout introduced in this PR. Regenerating the tree from the current repo state would avoid sending readers to wrong paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 4 - 72, The repository tree structure displayed in
the README.md file is outdated and contains stale file paths such as
formatDate.js, postUtils.js, and postUtilss.ts that no longer exist in the
repository. Additionally, it is missing new files that have been added in this
PR, such as frontend/src/pages/404.astro. Regenerate the entire directory tree
shown in the README.md starting from line 4 to line 72 to accurately reflect the
current state of the repository by running a tree generation command from the
project root and comparing it against the actual file structure, then update the
diff block with the correct and complete directory layout.
frontend/arquitectura.md-120-135 (1)

120-135: ⚠️ Potential issue | 🟡 Minor

Fix the broken import in the example.

Line 124 has a malformed import statement: import type { Post from "@domain/models/post"; is missing the closing }. Correct it to import type { Post } from "@domain/models/post"; so the documented example is actually usable.

🛠️ Suggested fix
-import type { Post from "`@domain/models/post`";
+import type { Post } from "`@domain/models/post`";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/arquitectura.md` around lines 120 - 135, The import statement in the
HeaderPost.astro code example is malformed with a missing closing brace. In the
import type statement for Post, add the closing brace `}` between `Post` and
`from` so the line reads `import type { Post } from "`@domain/models/post`";`
instead of the current broken syntax `import type { Post from
"`@domain/models/post`";`.
.gitignore-6-7 (1)

6-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Ignore the frontend package's env files too.

These entries only cover repo-root files. The Astro app lives under frontend/, so its env files are still trackable unless you mirror the ignore rules there.

Proposed fix
# frontend/.gitignore
+.env.development
+.env.production
+.env.local
+.env.*.local
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.gitignore around lines 6 - 7, The .gitignore file currently ignores
.env.development and .env.production only at the repository root level, but the
Astro frontend application located in the frontend/ directory has its own
environment files that are not covered by these rules. Add entries to .gitignore
that specifically ignore these environment files within the frontend/ directory
(such as frontend/.env.development and frontend/.env.production) to ensure the
frontend package's environment files are also excluded from version control.
frontend/src/styles/global.css-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the typo in the CSS variable name.

The variable --bg-tets appears to be a typo. It should likely be --bg-test or removed if unused.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/styles/global.css` at line 7, The CSS variable `--bg-tets`
contains a typo in its name. Change it to `--bg-test` to correct the spelling
and ensure consistency with the intended variable name.
frontend/src/components/organisms/Header.astro-21-21 (1)

21-21: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use English for alt text.

The alt text includes "Logotipo" which is Spanish. For consistency with the English codebase, use "Logo" instead.

📝 Proposed fix
-        alt="The Artemis Diary Logotipo"
+        alt="The Artemis Diary Logo"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/organisms/Header.astro` at line 21, The alt text for
the image in the Header.astro component contains the Spanish word "Logotipo"
instead of its English equivalent. Change the alt attribute value from "The
Artemis Diary Logotipo" to "The Artemis Diary Logo" to maintain consistency with
the English-language codebase.
frontend/src/utils/posts.ts-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use English for error messages.

The error message "Error al obtener los posts" is in Spanish. The codebase uses English elsewhere (comments, variable names, component text).

📝 Proposed fix
-  if (!res.ok) throw new Error(`Error al obtener los posts: ${res.status}`);
+  if (!res.ok) throw new Error(`Failed to fetch posts: ${res.status}`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/utils/posts.ts` at line 7, The error message in the throw
statement within the response validation check uses Spanish text "Error al
obtener los posts" which is inconsistent with the English language used
throughout the codebase. Replace the Spanish error message with its English
equivalent while keeping the same structure of including the HTTP status code in
the error message.
frontend/src/components/atoms/H3.astro-2-5 (1)

2-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make defaulted type props optional in the public contract.

Both components declare type as required while also defaulting it, which creates an inconsistent API surface for callers.

  • frontend/src/components/atoms/H3.astro#L2-L5: change type to optional (type?: "footer" | "related") so consumers can omit it and use the default.
  • frontend/src/components/atoms/PreTitle.astro#L2-L5: change type to optional (type?: "hero" | "related") to match the defaulted destructuring behavior.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/atoms/H3.astro` around lines 2 - 5, Make the type
property optional in both component Props interfaces to align with their
defaulted destructuring. In frontend/src/components/atoms/H3.astro at lines 2-5,
change the Props interface to make type optional by adding a question mark:
type?: "footer" | "related". In frontend/src/components/atoms/PreTitle.astro at
lines 2-5, similarly change the Props interface to make type optional: type?:
"hero" | "related". This ensures the public contract accurately reflects that
consumers can omit the type prop and rely on the default value.
frontend/src/components/organisms/RelatedArticles.astro-13-16 (1)

13-16: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align language for accessibility.

The aria-label is in Spanish ("Artículos relacionados") while the visible text is in English ("CONTINUE READING", "Related stories"). For consistency and better accessibility, either:

  • Use English for both: aria-label="Related articles"
  • Or use Spanish for both visible labels
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/organisms/RelatedArticles.astro` around lines 13 -
16, The aria-label attribute on the aside element contains Spanish text
("Artículos relacionados") while the visible text labels like PreTitle and H3
use English ("CONTINUE READING", "Related stories"), creating an accessibility
inconsistency. Change the aria-label to use English text that matches the
visible content language, specifically update it to "Related articles" to align
with the English content throughout the component.
frontend/src/layouts/BaseLayout.astro-33-33 (1)

33-33: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicate description meta tag.

The <meta name="description"> tag is declared twice (lines 33 and 40) with identical content. Remove the duplicate on line 40.

🧹 Proposed fix
     rel="stylesheet"
   />
-  <meta name="description" content={description} />
   <!-- Open Graph -->

Also applies to: 40-40

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/layouts/BaseLayout.astro` at line 33, The `<meta
name="description" content={description} />` tag is declared twice in
frontend/src/layouts/BaseLayout.astro with identical content at line 33 (anchor)
and line 40 (sibling). The anchor at line 33 requires no change and should be
kept. Remove the duplicate meta description tag at line 40 to eliminate the
redundancy.
frontend/src/layouts/BaseLayout.astro-55-55 (1)

55-55: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add conditional rendering for twitter:image:alt.

Line 55 conditionally renders twitter:image when ogImage is provided, but the corresponding twitter:image:alt tag (if it exists) should also be conditional on ogImageAlt. Currently, there's no twitter:image:alt tag at all—add it with the same conditional pattern as the Open Graph tags.

🏷️ Proposed fix
   <meta name="twitter:title" content={title} />
   <meta name="twitter:description" content={description} />
   {ogImage && <meta name="twitter:image" content={ogImage} />}
+  {ogImageAlt && <meta name="twitter:image:alt" content={ogImageAlt} />}
   <ClientRouter />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/layouts/BaseLayout.astro` at line 55, The BaseLayout.astro file
conditionally renders the twitter:image meta tag when ogImage is provided, but
is missing a corresponding twitter:image:alt meta tag. Add a new conditional
meta tag for twitter:image:alt that follows the same pattern as the existing
twitter:image tag (using the conditional syntax with ogImageAlt as the
condition), ensuring that the alt text for the Twitter image is provided
alongside the image itself.
frontend/README.md-72-77 (1)

72-77: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove placeholder "# test" headings.

Lines 72-77 contain six identical # test placeholder headings that serve no purpose and are flagged by markdownlint. Remove them to clean up the documentation.

🧹 Proposed fix
 ejecutar lint
 pnpm run lint
-# test
-# test
-# test
-# test
-# test
-# test
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/README.md` around lines 72 - 77, Remove the six duplicate `# test`
placeholder headings that appear consecutively in the README.md file. These
headings serve no documentation purpose and are flagged by markdownlint. Simply
delete all six lines containing only `# test` to clean up the documentation and
resolve the linting issues.
🧹 Nitpick comments (8)
frontend/arquitectura.md (1)

159-213: ⚡ Quick win

Remove the conflicting second architecture sketch.

This section introduces a different layout (presentation/, domain/ports, pages/api) after the earlier ui/application/infrastructure proposal, so the document now gives two incompatible sets of rules. Please consolidate on one canonical architecture or move the alternate sketch to a separate ADR/appendix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/arquitectura.md` around lines 159 - 213, The document contains two
incompatible architecture proposals: the earlier ui/application/infrastructure
layout and the newer domain/infrastructure/presentation/pages structure with
port/adapter pattern examples. Remove the conflicting second architecture sketch
that includes the file tree with domain/ports/use-cases and the
ArticleRepository interface and httpArticleRepository adapter code, either by
consolidating both approaches into a single coherent architecture document or by
moving the alternate proposal to a separate ADR or appendix section. Ensure the
remaining architecture provides one clear, canonical set of organizational rules
for the project.
frontend/src/pages/404.astro (2)

19-20: 💤 Low value

Consider extracting image dimensions as named constants.

The hardcoded division 2816 / 5 and 1536 / 5 makes the original image dimensions unclear. Consider defining these as constants with descriptive names for better maintainability.

♻️ Proposed refactor
 ---
 import BaseLayout from "../layouts/BaseLayout.astro";
 import base from "../utils/base";
 import { localImg } from "../utils/images";
 import { Image } from "astro:assets";
+
+const ORIGINAL_WIDTH = 2816;
+const ORIGINAL_HEIGHT = 1536;
+const SCALE_FACTOR = 5;
 ---

 <BaseLayout
   title="404 Not Found"
   description="The page you are looking for does not exist."
 >
   <div
     class="min-h-screen flex flex-col items-center justify-center text-center p-8"
   >
     <Image
       class=""
       src={localImg(`/images/404.png`, base)}
       alt="404 Not Found"
-      width={2816 / 5}
-      height={1536 / 5}
+      width={ORIGINAL_WIDTH / SCALE_FACTOR}
+      height={ORIGINAL_HEIGHT / SCALE_FACTOR}
     />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/404.astro` around lines 19 - 20, The image width and
height properties in the 404.astro file use hardcoded mathematical expressions
(2816 / 5 and 1536 / 5) that obscure the original image dimensions. Create named
constants at the top of the component (or in a constants section) that define
the original image width (2816), original image height (1536), and the scaling
factor (5) with descriptive names that clarify their purpose. Then replace the
hardcoded divisions in the width and height properties with references to these
constants. This will improve code clarity and make it easier to adjust
dimensions in the future.

16-16: 💤 Low value

Remove the empty class attribute.

The empty class="" attribute on line 16 serves no purpose and can be removed for cleaner markup.

♻️ Proposed cleanup
     <Image
-      class=""
       src={localImg(`/images/404.png`, base)}
       alt="404 Not Found"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/404.astro` at line 16, Remove the empty class attribute
(class="") on the element in the 404.astro file. The attribute provides no
styling value and serves no purpose, so simply delete it to clean up the markup.
frontend/src/styles/global.css (1)

73-86: ⚡ Quick win

Consider using CSS custom properties instead of hardcoded colors.

The .content descendant selectors use hardcoded #ccc color values instead of the defined CSS custom properties like --color-text or --color-text-muted. This reduces maintainability and theme consistency.

♻️ Proposed refactor
 .content h2 {
   font-size: 1.4rem;
   margin: 2rem 0 0.75rem;
-  color: `#ccc`;
+  color: var(--color-text-muted);
 }
 .content h3 {
   font-size: 1.1rem;
   margin: 1.5rem 0 0.5rem;
-  color: `#ccc`;
+  color: var(--color-text-muted);
 }
 .content p {
   margin-bottom: 1rem;
-  color: `#ccc`;
+  color: var(--color-text-muted);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/styles/global.css` around lines 73 - 86, Replace the hardcoded
color value `#ccc` with CSS custom properties in the `.content h2`, `.content
h3`, and `.content p` selectors. Instead of `color: `#ccc`;`, use `color:
var(--color-text-muted);` or an appropriate existing CSS custom property defined
elsewhere in your stylesheet to ensure consistency and maintainability across
theme changes.
frontend/src/components/molecules/RelatedArticlesList.astro (1)

13-13: 💤 Low value

Consider using a more semantic element than <nav>.

Using <nav> for related articles is semantically questionable. The <nav> element is intended for major navigation blocks (site navigation, table of contents). Since the parent component RelatedArticles.astro already wraps this in <aside>, and these are just related article links rather than site navigation, consider using a <div> or removing this wrapper entirely.

♻️ Proposed change
-<nav>
   <ul
     class="grid grid-cols-[repeat(auto-fit,minmax(350px,1fr))] gap-0 px-6 list-none"
   >
     {
       relatedPosts.map((relatedPost) => (
         <li>
           <PostCard post={relatedPost} isRelated={true} />
         </li>
       ))
     }
   </ul>
-</nav>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/molecules/RelatedArticlesList.astro` at line 13, The
`<nav>` element in RelatedArticlesList.astro is semantically incorrect for a
list of related article links, as `<nav>` should be reserved for major
navigation blocks like site navigation or tables of contents. Since the parent
RelatedArticles component already wraps the content in an `<aside>` element,
replace the `<nav>` wrapper with a `<div>` or remove the wrapper entirely to
better represent the semantic intent of related articles rather than site
navigation.
frontend/src/utils/posts.ts (1)

25-26: ⚡ Quick win

Extract magic number to named constant.

The limit of 3 related posts is hardcoded. The comment suggests converting it to a constant, which would improve maintainability.

♻️ Proposed refactor
+const MAX_RELATED_POSTS = 3;
+
 export function getRelatedPostByFirstTag(
   tags: string,
   allPosts: Post[],
 ): Post[] {
   const firstTag = String(tags).split(",")[0].trim().toLowerCase();
   return allPosts
     .filter(
       (articulo) =>
         articulo.tags !== tags &&
         String(articulo.tags)
           .split(",")
           .map((tramo) => tramo.trim().toLowerCase())
           .includes(firstTag),
     )
-    .slice(0, 3);
-  //se puede convertir en constante
+    .slice(0, MAX_RELATED_POSTS);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/utils/posts.ts` around lines 25 - 26, The hardcoded number 3 in
the slice method call is a magic number that reduces code maintainability.
Create a named constant (such as RELATED_POSTS_LIMIT) with the value 3 at the
top of the file, then replace the hardcoded 3 in the .slice(0, 3) expression
with this constant to make the code more maintainable and easier to modify in
the future.
frontend/src/utils/posts.test.ts (1)

5-21: ⚡ Quick win

Consider adding test coverage for additional edge cases.

The current suite covers the happy path (3 matches) and empty tags, but could benefit from:

  • A case where fewer than 3 related posts exist
  • A case where no posts share the first tag

These scenarios would strengthen confidence in the slice(0, 3) behavior and empty-result handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/utils/posts.test.ts` around lines 5 - 21, The test suite for
getRelatedPostByFirstTag function in the describe block is missing edge case
coverage. Add two new test cases to strengthen confidence in the slice(0, 3)
behavior: first, a test case where fewer than 3 related posts exist that share
the first tag (verify it returns all matches without padding to 3), and second,
a test case where no posts in the data share the first tag at all (verify it
returns an empty array). Use mockPostsData or create appropriate test data to
set up these scenarios alongside the existing happy path and empty tags test
cases.
frontend/README.md (1)

65-71: 💤 Low value

Consider organizing deployment instructions more clearly.

The deployment section mixes bash examples (lines 50-61) with a PowerShell example (line 68) and Spanish instructions (lines 65-71). For better clarity, consider:

  • Grouping platform-specific examples under subheadings (e.g., "### Bash/Linux", "### PowerShell/Windows")
  • Using consistent language (English throughout, or clearly marked Spanish sections)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/README.md` around lines 65 - 71, The deployment instructions section
mixes bash, PowerShell, and Spanish language examples without clear
organization, making it difficult for users to find platform-specific guidance.
Reorganize the deployment instructions by creating platform-specific subheadings
(such as "Bash/Linux" and "PowerShell/Windows") and group the corresponding
examples under each heading. Additionally, standardize the language throughout
by either using English consistently or clearly marking sections that use
Spanish so users can quickly identify instructions relevant to their platform
and language preference.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.husky/pre-commit:
- Line 1: The pre-commit hook in .husky/pre-commit is hanging because pnpm test
runs vitest in watch mode by default in interactive terminals. Replace the pnpm
test command with pnpm test -- --run to force a single test run that exits after
completion instead of entering watch mode, allowing the pre-commit hook to
complete successfully.

In `@frontend/eslint.config.js`:
- Around line 37-40: The Astro flat-config block in the eslint.config.js file
uses string package names for parsers on lines 37 and 39, but ESLint flat config
requires parser objects to be imported module instances, not strings. Import
both astro-eslint-parser and `@typescript-eslint/parser` modules at the top of the
file, then replace the string values for parser: "astro-eslint-parser" and
parserOptions.parser: "`@typescript-eslint/parser`" with references to the
imported parser objects. This ensures the configuration complies with ESLint
flat-config requirements and will not fail during linting and pre-commit hooks.

In `@frontend/src/components/molecules/RelatedArticlesList.astro`:
- Line 10: The RelatedArticlesList.astro component calls getPosts() on every
render, causing duplicate API calls in pages that already fetch posts. To fix
this, modify the component to accept a posts prop instead of calling getPosts()
directly. Update the getRelatedPostByFirstTag call to use the posts prop that's
passed in rather than calling getPosts(). Then update the parent component
RelatedArticles.astro to receive and forward the allPosts prop, and ensure all
pages that use RelatedArticles pass the posts they've already fetched to
eliminate redundant API calls during build.

In `@frontend/src/components/organisms/GridPost.astro`:
- Line 5: In GridPost.astro, modify the component to accept posts as a prop
instead of always calling getPosts() independently. Change the constant
definition to use the posts prop with a fallback operator (??), calling
getPosts() only if no posts prop is provided. This eliminates duplicate
getPosts() calls when multiple components on the same page fetch posts while
maintaining backward compatibility for cases where the component is used without
a posts prop being passed.

In `@frontend/src/components/organisms/HeaderPost.astro`:
- Around line 18-25: The img element in HeaderPost.astro uses the h-120 class
which is not a valid Tailwind CSS v4 class (the default scale maxes out at
h-96). Replace the h-120 class with a valid Tailwind height class such as h-96,
or add custom height configuration to the project's Tailwind theme if a specific
height beyond the default scale is required. Given the max-h-80 constraint on
the parent figure element, a standard height class like h-96 would be
appropriate.

In `@frontend/src/pages/posts/`[slug].astro:
- Around line 44-47: Fix the HTML tag typo by replacing the opening tag from
<spam> to <span> in the set:html template expression. Additionally, address the
XSS vulnerability by ensuring that post.content is sanitized server-side before
being rendered with set:html. If the content comes from untrusted sources,
implement server-side sanitization using a library or safely render Markdown
instead of directly injecting raw HTML content.
- Around line 10-12: The fetch call and .json() parsing in the getStaticPaths
function have no error handling, which means API failures or invalid JSON
responses during build will result in unclear error messages. Wrap both the
fetch operation and the .json() parsing in a try-catch block to catch any
network errors or JSON parsing errors, and throw or log a descriptive error
message that includes details about the failure (such as the HTTP status,
network error, or parsing error). This will make it clear to developers whether
the API is unavailable, returns an error status, or returns invalid JSON.

In `@frontend/src/styles/global.css`:
- Line 54: The clamp function in the caption utility CSS rule contains two
syntax errors that need to be corrected. First, change the invalid CSS unit `1w`
to the valid viewport width unit `1vw`. Second, add spaces around the `+`
operator in the middle parameter to match proper CSS syntax formatting (changing
`0.9rem+1w` to `0.9rem + 1vw`). The corrected clamp function should have proper
spacing and valid units throughout.

In `@frontend/src/utils/posts.ts`:
- Around line 7-8: Add response shape validation to the function that fetches
posts. After calling res.json(), validate that the returned data is actually an
array of Post objects with the expected structure and properties. If the
response data doesn't match the expected Post[] shape, throw a descriptive error
that clearly indicates the API returned unexpected data format. This validation
should occur before returning the response to prevent downstream components from
receiving malformed data and failing with cryptic errors.
- Line 6: The fetch call in the posts.ts file at the PUBLIC_API_BASE endpoint
has no timeout mechanism, which can cause the build to hang indefinitely if the
API is slow or unreachable. Add a timeout by creating an AbortController,
setting a timeout (e.g., 5-10 seconds) that calls abort() on the controller, and
passing the signal from the controller in the fetch options as the second
parameter. This ensures the request will be cancelled if it exceeds the timeout
duration.

---

Minor comments:
In @.gitignore:
- Around line 6-7: The .gitignore file currently ignores .env.development and
.env.production only at the repository root level, but the Astro frontend
application located in the frontend/ directory has its own environment files
that are not covered by these rules. Add entries to .gitignore that specifically
ignore these environment files within the frontend/ directory (such as
frontend/.env.development and frontend/.env.production) to ensure the frontend
package's environment files are also excluded from version control.

In `@frontend/arquitectura.md`:
- Around line 120-135: The import statement in the HeaderPost.astro code example
is malformed with a missing closing brace. In the import type statement for
Post, add the closing brace `}` between `Post` and `from` so the line reads
`import type { Post } from "`@domain/models/post`";` instead of the current broken
syntax `import type { Post from "`@domain/models/post`";`.

In `@frontend/README.md`:
- Around line 72-77: Remove the six duplicate `# test` placeholder headings that
appear consecutively in the README.md file. These headings serve no
documentation purpose and are flagged by markdownlint. Simply delete all six
lines containing only `# test` to clean up the documentation and resolve the
linting issues.

In `@frontend/src/components/atoms/H3.astro`:
- Around line 2-5: Make the type property optional in both component Props
interfaces to align with their defaulted destructuring. In
frontend/src/components/atoms/H3.astro at lines 2-5, change the Props interface
to make type optional by adding a question mark: type?: "footer" | "related". In
frontend/src/components/atoms/PreTitle.astro at lines 2-5, similarly change the
Props interface to make type optional: type?: "hero" | "related". This ensures
the public contract accurately reflects that consumers can omit the type prop
and rely on the default value.

In `@frontend/src/components/organisms/Header.astro`:
- Line 21: The alt text for the image in the Header.astro component contains the
Spanish word "Logotipo" instead of its English equivalent. Change the alt
attribute value from "The Artemis Diary Logotipo" to "The Artemis Diary Logo" to
maintain consistency with the English-language codebase.

In `@frontend/src/components/organisms/RelatedArticles.astro`:
- Around line 13-16: The aria-label attribute on the aside element contains
Spanish text ("Artículos relacionados") while the visible text labels like
PreTitle and H3 use English ("CONTINUE READING", "Related stories"), creating an
accessibility inconsistency. Change the aria-label to use English text that
matches the visible content language, specifically update it to "Related
articles" to align with the English content throughout the component.

In `@frontend/src/layouts/BaseLayout.astro`:
- Line 33: The `<meta name="description" content={description} />` tag is
declared twice in frontend/src/layouts/BaseLayout.astro with identical content
at line 33 (anchor) and line 40 (sibling). The anchor at line 33 requires no
change and should be kept. Remove the duplicate meta description tag at line 40
to eliminate the redundancy.
- Line 55: The BaseLayout.astro file conditionally renders the twitter:image
meta tag when ogImage is provided, but is missing a corresponding
twitter:image:alt meta tag. Add a new conditional meta tag for twitter:image:alt
that follows the same pattern as the existing twitter:image tag (using the
conditional syntax with ogImageAlt as the condition), ensuring that the alt text
for the Twitter image is provided alongside the image itself.

In `@frontend/src/styles/global.css`:
- Line 7: The CSS variable `--bg-tets` contains a typo in its name. Change it to
`--bg-test` to correct the spelling and ensure consistency with the intended
variable name.

In `@frontend/src/utils/posts.ts`:
- Line 7: The error message in the throw statement within the response
validation check uses Spanish text "Error al obtener los posts" which is
inconsistent with the English language used throughout the codebase. Replace the
Spanish error message with its English equivalent while keeping the same
structure of including the HTTP status code in the error message.

In `@README.md`:
- Around line 4-72: The repository tree structure displayed in the README.md
file is outdated and contains stale file paths such as formatDate.js,
postUtils.js, and postUtilss.ts that no longer exist in the repository.
Additionally, it is missing new files that have been added in this PR, such as
frontend/src/pages/404.astro. Regenerate the entire directory tree shown in the
README.md starting from line 4 to line 72 to accurately reflect the current
state of the repository by running a tree generation command from the project
root and comparing it against the actual file structure, then update the diff
block with the correct and complete directory layout.

---

Nitpick comments:
In `@frontend/arquitectura.md`:
- Around line 159-213: The document contains two incompatible architecture
proposals: the earlier ui/application/infrastructure layout and the newer
domain/infrastructure/presentation/pages structure with port/adapter pattern
examples. Remove the conflicting second architecture sketch that includes the
file tree with domain/ports/use-cases and the ArticleRepository interface and
httpArticleRepository adapter code, either by consolidating both approaches into
a single coherent architecture document or by moving the alternate proposal to a
separate ADR or appendix section. Ensure the remaining architecture provides one
clear, canonical set of organizational rules for the project.

In `@frontend/README.md`:
- Around line 65-71: The deployment instructions section mixes bash, PowerShell,
and Spanish language examples without clear organization, making it difficult
for users to find platform-specific guidance. Reorganize the deployment
instructions by creating platform-specific subheadings (such as "Bash/Linux" and
"PowerShell/Windows") and group the corresponding examples under each heading.
Additionally, standardize the language throughout by either using English
consistently or clearly marking sections that use Spanish so users can quickly
identify instructions relevant to their platform and language preference.

In `@frontend/src/components/molecules/RelatedArticlesList.astro`:
- Line 13: The `<nav>` element in RelatedArticlesList.astro is semantically
incorrect for a list of related article links, as `<nav>` should be reserved for
major navigation blocks like site navigation or tables of contents. Since the
parent RelatedArticles component already wraps the content in an `<aside>`
element, replace the `<nav>` wrapper with a `<div>` or remove the wrapper
entirely to better represent the semantic intent of related articles rather than
site navigation.

In `@frontend/src/pages/404.astro`:
- Around line 19-20: The image width and height properties in the 404.astro file
use hardcoded mathematical expressions (2816 / 5 and 1536 / 5) that obscure the
original image dimensions. Create named constants at the top of the component
(or in a constants section) that define the original image width (2816),
original image height (1536), and the scaling factor (5) with descriptive names
that clarify their purpose. Then replace the hardcoded divisions in the width
and height properties with references to these constants. This will improve code
clarity and make it easier to adjust dimensions in the future.
- Line 16: Remove the empty class attribute (class="") on the element in the
404.astro file. The attribute provides no styling value and serves no purpose,
so simply delete it to clean up the markup.

In `@frontend/src/styles/global.css`:
- Around line 73-86: Replace the hardcoded color value `#ccc` with CSS custom
properties in the `.content h2`, `.content h3`, and `.content p` selectors.
Instead of `color: `#ccc`;`, use `color: var(--color-text-muted);` or an
appropriate existing CSS custom property defined elsewhere in your stylesheet to
ensure consistency and maintainability across theme changes.

In `@frontend/src/utils/posts.test.ts`:
- Around line 5-21: The test suite for getRelatedPostByFirstTag function in the
describe block is missing edge case coverage. Add two new test cases to
strengthen confidence in the slice(0, 3) behavior: first, a test case where
fewer than 3 related posts exist that share the first tag (verify it returns all
matches without padding to 3), and second, a test case where no posts in the
data share the first tag at all (verify it returns an empty array). Use
mockPostsData or create appropriate test data to set up these scenarios
alongside the existing happy path and empty tags test cases.

In `@frontend/src/utils/posts.ts`:
- Around line 25-26: The hardcoded number 3 in the slice method call is a magic
number that reduces code maintainability. Create a named constant (such as
RELATED_POSTS_LIMIT) with the value 3 at the top of the file, then replace the
hardcoded 3 in the .slice(0, 3) expression with this constant to make the code
more maintainable and easier to modify in the future.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c1be9b7-ea15-4f43-ae3f-1932c782b608

📥 Commits

Reviewing files that changed from the base of the PR and between 5c00b64 and c8e4c0f.

⛔ Files ignored due to path filters (14)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • frontend/public/favicon.ico is excluded by !**/*.ico
  • frontend/public/favicon.svg is excluded by !**/*.svg
  • frontend/public/images/404.png is excluded by !**/*.png
  • frontend/public/images/art002e000191~large.jpg is excluded by !**/*.jpg
  • frontend/public/images/art002e008487~large.jpg is excluded by !**/*.jpg
  • frontend/public/images/art002e009287~large.jpg is excluded by !**/*.jpg
  • frontend/public/images/art002e009301~large.jpg is excluded by !**/*.jpg
  • frontend/public/images/art002e012278~large.jpg is excluded by !**/*.jpg
  • frontend/public/images/art002e016198~large.jpg is excluded by !**/*.jpg
  • frontend/public/images/artemis-logo.png is excluded by !**/*.png
  • frontend/public/images/artemis-mission.jpg is excluded by !**/*.jpg
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • .gitignore
  • .husky/pre-commit
  • .vscode/settings.json
  • README.md
  • frontend/.gitignore
  • frontend/.prettierrc
  • frontend/.vscode/extensions.json
  • frontend/.vscode/launch.json
  • frontend/README.md
  • frontend/arquitectura.md
  • frontend/astro.config.mjs
  • frontend/eslint.config.js
  • frontend/index.html
  • frontend/package.json
  • frontend/src/components/atoms/DatePublished.astro
  • frontend/src/components/atoms/H1.astro
  • frontend/src/components/atoms/H3.astro
  • frontend/src/components/atoms/Img.astro
  • frontend/src/components/atoms/PreTitle.astro
  • frontend/src/components/molecules/PostCard.astro
  • frontend/src/components/molecules/RelatedArticlesList.astro
  • frontend/src/components/organisms/Footer.astro
  • frontend/src/components/organisms/GridPost.astro
  • frontend/src/components/organisms/Header.astro
  • frontend/src/components/organisms/HeaderPost.astro
  • frontend/src/components/organisms/Hero.astro
  • frontend/src/components/organisms/RelatedArticles.astro
  • frontend/src/layouts/BaseLayout.astro
  • frontend/src/mocks/mock-posts-data.ts
  • frontend/src/pages/404.astro
  • frontend/src/pages/index.astro
  • frontend/src/pages/posts/[slug].astro
  • frontend/src/styles/global.css
  • frontend/src/types/cover-image.ts
  • frontend/src/types/img-props.ts
  • frontend/src/types/post.ts
  • frontend/src/utils/base.ts
  • frontend/src/utils/date.test.ts
  • frontend/src/utils/date.ts
  • frontend/src/utils/images.ts
  • frontend/src/utils/posts.test.ts
  • frontend/src/utils/posts.ts
  • frontend/tsconfig.json
  • package.json
  • pnpm-workspace.yaml
💤 Files with no reviewable changes (1)
  • frontend/index.html

Comment thread .husky/pre-commit
@@ -0,0 +1 @@
cd frontend && pnpm run lint && pnpm test

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find the frontend directory and check package.json
find . -name "package.json" -path "*/frontend/*" | head -20

Repository: biko2/artemis-blog

Length of output: 86


🏁 Script executed:

#!/bin/bash
# Also check the .husky directory structure
ls -la .husky/

Repository: biko2/artemis-blog

Length of output: 230


🏁 Script executed:

#!/bin/bash
# Look for vitest configuration
find . -name "vitest.config.*" -o -name "vite.config.*" | grep -v node_modules

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Check the test script in frontend/package.json
cat ./frontend/package.json | grep -A 5 '"test"'

Repository: biko2/artemis-blog

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Look for vitest config in frontend directory
ls -la ./frontend/ | grep -E "vitest|vite"

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Read the full frontend package.json to see all scripts
cat ./frontend/package.json

Repository: biko2/artemis-blog

Length of output: 915


🏁 Script executed:

#!/bin/bash
# Check root directory for vite/vitest config that might apply to frontend
ls -la | grep -E "vite|vitest"

Repository: biko2/artemis-blog

Length of output: 44


🌐 Web query:

vitest default behavior watch mode interactive shell --run flag cli

💡 Result:

In Vitest, the default behavior for watch mode is determined by whether the environment is interactive [1][2]. By default, Vitest enables watch mode if the environment is interactive, specifically defined by the condition:!process.env.CI && process.stdin.isTTY [1][2]. Key details regarding this behavior and the --run flag include: 1. Interactive Environments: When running in a terminal (TTY), Vitest defaults to watch mode, which keeps the process alive to monitor files for changes and re-run tests [3][4]. 2. Non-Interactive Environments: If the environment is CI or the standard input is not a TTY (non-interactive), Vitest defaults to run mode [1][2][5]. This change ensures the process exits after the test suite completes, preventing it from hanging in environments where there is no user to interact with the shell [5][4]. 3. The --run Flag: Passing the --run flag explicitly forces Vitest to perform a single run and then exit, effectively disabling watch mode [3][6][7]. This is useful for CI/CD pipelines, pre-commit hooks (like lint-staged), or any scenario where you want Vitest to complete and exit regardless of the default environment detection [3][8][9]. You cannot use the --run flag if --watch is explicitly enabled [3]. Conversely, if you are in a non-interactive environment but still wish to use watch mode, you can explicitly enable it using the --watch flag [1][2].

Citations:


Switch the pre-commit hook to a one-shot Vitest run.

pnpm test resolves to vitest, which defaults to watch mode in interactive terminals. This causes the pre-commit hook to hang instead of exiting. Use pnpm test -- --run to force a single test run and exit.

Proposed fix
-cd frontend && pnpm run lint && pnpm test
+cd frontend && pnpm run lint && pnpm test -- --run
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cd frontend && pnpm run lint && pnpm test
cd frontend && pnpm run lint && pnpm test -- --run
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.husky/pre-commit at line 1, The pre-commit hook in .husky/pre-commit is
hanging because pnpm test runs vitest in watch mode by default in interactive
terminals. Replace the pnpm test command with pnpm test -- --run to force a
single test run that exits after completion instead of entering watch mode,
allowing the pre-commit hook to complete successfully.

Comment thread frontend/eslint.config.js
Comment on lines +37 to +40
parser: "astro-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
extraFileExtensions: [".astro"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

For ESLint flat config, should languageOptions.parserand nestedparserOptions.parserbe parser objects/functions (module imports) rather than string package names when usingastro-eslint-parserwith@typescript-eslint/parser?

💡 Result:

In ESLint flat config, both languageOptions.parser and nested parserOptions.parser should be imported parser objects (or modules) rather than string package names [1][2][3]. 1. languageOptions.parser: ESLint flat config explicitly requires the parser to be an object that conforms to the parser interface (containing a parse or parseForESLint method) [3][4]. Providing a string package name, which was common in the legacy.eslintrc system, is no longer supported for this property [3][5][6]. You must import the parser module (e.g., import * as parser from "astro-eslint-parser") and assign the object directly [2][7][8]. 2. Nested parserOptions.parser: When using astro-eslint-parser, the parserOptions.parser property is passed to the astro-eslint-parser to handle script blocks within.astro files [9][10]. This parser also expects an object reference rather than a string [7]. Similar to the top-level parser, you should import the @typescript-eslint/parser module and assign it as an object [7]. Example of correct flat config structure: import * as astroParser from "astro-eslint-parser"; import tsParser from "@typescript-eslint/parser"; export default [ { files: ["/*.astro"], languageOptions: { parser: astroParser, parserOptions: { parser: tsParser, extraFileExtensions: [".astro"], }, }, }, ];

Citations:


🏁 Script executed:

find . -name "eslint.config.js" -type f

Repository: biko2/artemis-blog

Length of output: 90


🏁 Script executed:

head -50 frontend/eslint.config.js

Repository: biko2/artemis-blog

Length of output: 1401


Import parser objects instead of using string package names in the Astro flat-config block.

Lines 37 and 39 currently use string package names for parsers, which violates ESLint flat-config requirements. ESLint flat config explicitly requires languageOptions.parser and parserOptions.parser to be imported parser objects (modules), not strings. The current setup will fail during linting and pre-commit hooks.

Additionally, astro-eslint-parser is not imported at all and must be added.

Suggested fix
+import astroParser from "astro-eslint-parser";
 import tsParser from "`@typescript-eslint/parser`";
 import tsPlugin from "`@typescript-eslint/eslint-plugin`";
 import astro from "eslint-plugin-astro";
@@
   {
     files: ["**/*.astro"],
     languageOptions: {
-      parser: "astro-eslint-parser",
+      parser: astroParser,
       parserOptions: {
-        parser: "`@typescript-eslint/parser`",
+        parser: tsParser,
         extraFileExtensions: [".astro"],
         ecmaVersion: "latest",
         sourceType: "module",
         project: "./tsconfig.json",
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/eslint.config.js` around lines 37 - 40, The Astro flat-config block
in the eslint.config.js file uses string package names for parsers on lines 37
and 39, but ESLint flat config requires parser objects to be imported module
instances, not strings. Import both astro-eslint-parser and
`@typescript-eslint/parser` modules at the top of the file, then replace the
string values for parser: "astro-eslint-parser" and parserOptions.parser:
"`@typescript-eslint/parser`" with references to the imported parser objects. This
ensures the configuration complies with ESLint flat-config requirements and will
not fail during linting and pre-commit hooks.

}

const { tags } = Astro.props;
const relatedPosts = getRelatedPostByFirstTag(tags, await getPosts());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Eliminate duplicate getPosts() call by accepting posts as prop.

This component calls getPosts() on every render. When used in pages that already fetch posts (e.g., posts/[slug].astro fetches in getStaticPaths), this causes duplicate API calls during build. This wastes build time and could cause issues if the API has rate limits.

♻️ Proposed fix to accept posts as prop
 interface Props {
   tags: string;
+  allPosts: Post[];
 }

-const { tags } = Astro.props;
-const relatedPosts = getRelatedPostByFirstTag(tags, await getPosts());
+const { tags, allPosts } = Astro.props;
+const relatedPosts = getRelatedPostByFirstTag(tags, allPosts);

Then update the parent component RelatedArticles.astro to pass allPosts prop, and ensure calling pages pass the posts they've already fetched.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/molecules/RelatedArticlesList.astro` at line 10, The
RelatedArticlesList.astro component calls getPosts() on every render, causing
duplicate API calls in pages that already fetch posts. To fix this, modify the
component to accept a posts prop instead of calling getPosts() directly. Update
the getRelatedPostByFirstTag call to use the posts prop that's passed in rather
than calling getPosts(). Then update the parent component RelatedArticles.astro
to receive and forward the allPosts prop, and ensure all pages that use
RelatedArticles pass the posts they've already fetched to eliminate redundant
API calls during build.

import type { Post } from "../../types/post";
import PostCard from "../molecules/PostCard.astro";
import { getPosts } from "../../utils/posts";
const posts = await getPosts();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Eliminate duplicate getPosts() call by accepting posts as prop.

This component calls getPosts() independently. When multiple components fetch posts on the same page (e.g., GridPost and RelatedArticlesList), build time increases unnecessarily. This is the same root cause as the issue in RelatedArticlesList.astro.

♻️ Proposed fix to accept posts as prop
+interface Props {
+  posts?: Post[];
+}
+
-const posts = await getPosts();
+const { posts: propPosts } = Astro.props;
+const posts = propPosts ?? await getPosts();

This allows parent pages to pass already-fetched posts while maintaining backward compatibility with ?? fallback.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const posts = await getPosts();
interface Props {
posts?: Post[];
}
const { posts: propPosts } = Astro.props;
const posts = propPosts ?? await getPosts();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/organisms/GridPost.astro` at line 5, In
GridPost.astro, modify the component to accept posts as a prop instead of always
calling getPosts() independently. Change the constant definition to use the
posts prop with a fallback operator (??), calling getPosts() only if no posts
prop is provided. This eliminates duplicate getPosts() calls when multiple
components on the same page fetch posts while maintaining backward compatibility
for cases where the component is used without a posts prop being passed.

Comment on lines +18 to +25
<figure class="w-full max-h-80 overflow-hidden">
<img
transition:name={`picture-${post.id}`}
class="w-full h-120 object-cover"
src={localImg(post.coverImage.src, base)}
alt={post.coverImage.alt}
/>
</figure>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if other image components use similar crop patterns

rg -nP 'max-h-\d+.*overflow-hidden' --type=astro -A2 -B2

Repository: biko2/artemis-blog

Length of output: 92


🏁 Script executed:

# Read the HeaderPost.astro file to see the full context
cat -n frontend/src/components/organisms/HeaderPost.astro

Repository: biko2/artemis-blog

Length of output: 1416


🏁 Script executed:

# Search for .astro files with overflow-hidden and max-h patterns
rg 'max-h-\d+.*overflow-hidden' frontend/ -A2 -B2

Repository: biko2/artemis-blog

Length of output: 469


🏁 Script executed:

# Check for Tailwind configuration to verify h-120 and max-h-80 values
find . -name "tailwind.config.*" -o -name "tailwind.config.ts" -o -name "tailwind.config.js"

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Search for h-120 and h-80 usage in .astro files
rg 'h-120|h-80' frontend/ --type-list | head -20 && rg 'h-120|h-80' frontend/

Repository: biko2/artemis-blog

Length of output: 663


🏁 Script executed:

# Find tailwind config files
find frontend/ -name "tailwind.config.*" -o -name "*.config.ts" | grep -E "(tailwind|config)"

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Search for any comments about image sizing or height classes
rg 'h-\d+|max-h' frontend/src --type astro -B1 -A1 | head -50

Repository: biko2/artemis-blog

Length of output: 92


🏁 Script executed:

# Check if there are other img elements with similar patterns
rg '<img' frontend/src/components --type astro -A3 | head -80

Repository: biko2/artemis-blog

Length of output: 92


🏁 Script executed:

# Look for the tailwind config in the root or common locations
find . -maxdepth 3 -name "*tailwind*" -type f 2>/dev/null | grep -v node_modules

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Look for tailwind config in root and common locations
ls -la | grep -E "(tailwind|config)"
find . -maxdepth 2 -name "tailwind*" -o -name "astro.config*" 2>/dev/null | head -20

Repository: biko2/artemis-blog

Length of output: 90


🏁 Script executed:

# Search for h-120 definition in config files
rg 'h-120|extend|height' --type-list 2>/dev/null || true
rg 'h-120' . -g '*.ts' -g '*.js' -g '*.json' 2>/dev/null | head -20

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Check astro config
cat astro.config.mjs 2>/dev/null || cat astro.config.ts 2>/dev/null || echo "No astro config in root"

Repository: biko2/artemis-blog

Length of output: 86


🏁 Script executed:

# Search for other image components to see if there's a pattern for sizing
grep -r "object-cover" frontend/src/components --include="*.astro" -B2 -A2

Repository: biko2/artemis-blog

Length of output: 950


🏁 Script executed:

# Check the astro config for tailwind setup
cat frontend/astro.config.mjs

Repository: biko2/artemis-blog

Length of output: 662


🏁 Script executed:

# Search for tailwind config in frontend directory
find frontend -name "tailwind.config.*" -o -name "tailwind.config.js" -o -name "tailwind.config.ts"

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Look for any tailwind setup or config references
rg 'tailwind|h-120' frontend --type-list 2>/dev/null || rg 'tailwind|h-120' frontend -g '*.mjs' -g '*.ts' -g '*.js' -g '*.json' 2>/dev/null | head -30

Repository: biko2/artemis-blog

Length of output: 1333


🏁 Script executed:

# Check postcss config
cat frontend/postcss.config.mjs 2>/dev/null || cat frontend/postcss.config.js 2>/dev/null || cat postcss.config.mjs 2>/dev/null || cat postcss.config.js 2>/dev/null || echo "No postcss config found"

Repository: biko2/artemis-blog

Length of output: 86


🏁 Script executed:

# Check if h-120 is defined anywhere or if it's just a typo
rg 'h-120|height.*120' frontend -g '*.css' -g '*.scss' -g '*.json'

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Check the global CSS or styles to see if h-120 is customized
find frontend/src -name "*.css" -o -name "*.scss" | xargs cat 2>/dev/null

Repository: biko2/artemis-blog

Length of output: 3782


🏁 Script executed:

# Verify standard Tailwind height values - check what the highest default is
rg 'h-96|h-screen' frontend/src/components -l

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Look at the entire CSS setup and see if there's a custom theme
cat frontend/src/styles/global.css 2>/dev/null || cat frontend/src/global.css 2>/dev/null || ls -la frontend/src/styles/ 2>/dev/null || echo "Checking for style files..."

Repository: biko2/artemis-blog

Length of output: 3782


🏁 Script executed:

# Check if there's a tailwind config extending theme with h-120
find frontend -name "*.config.*" -type f | xargs grep -l "height\|h-120" 2>/dev/null

Repository: biko2/artemis-blog

Length of output: 46


🏁 Script executed:

# Look for any tailwind theme/extend configuration
rg 'extend|theme|height' frontend -g '*.ts' -g '*.js' -g '*.json' -B2 -A2 | head -60

Repository: biko2/artemis-blog

Length of output: 448


🏁 Script executed:

# Verify if h-120 actually gets compiled into CSS by checking if it appears anywhere
rg 'h-120' frontend

Repository: biko2/artemis-blog

Length of output: 153


🏁 Script executed:

# Check the standard Tailwind v4 default height scale
web_search
Tailwind CSS v4 default height scale h-120 maximum values

Repository: biko2/artemis-blog

Length of output: 160


🌐 Web query:

Tailwind CSS v4 default height scale maximum h- class values

💡 Result:

Tailwind CSS v4 default maximum height utility in the spacing-based scale is 96, i.e. the largest built-in numeric class is h-96 (height) / max-h-96 (max-height), which maps to 24rem (384px at the default 16px root) based on the default spacing scale [1][2][3]. Answering the “maximum h- class values” part: - h-* numeric values include h-96 as the largest default spacing-step (24rem) [1]. - For max-h-*, the largest default spacing-step is also max-h-96 (24rem) [2]. - h-auto, h-full, h-screen, h-dvh, h-lvh, etc. are additional non-spacing-based options, so “maximum” can be interpreted as either “largest numeric spacing class” or “largest available semantic viewport-based option” [1][2]. If you want a hard ceiling beyond 96, you can use arbitrary values (h-[...]) or customize the spacing theme; the utilities are driven by the --spacing theme variable [1][4].

Citations:


🏁 Script executed:

# Check if h-120 is ever defined or extended in the codebase
rg 'extend.*height|height.*120|h.120' frontend --type-list 2>/dev/null || rg 'extend.*height|height.*120|h.120' frontend -g '*.astro' -g '*.ts' -g '*.js' -g '*.json' -g '*.css'

Repository: biko2/artemis-blog

Length of output: 153


🏁 Script executed:

# Verify what classes are actually available in the project - check if there are any custom theme extensions
rg '`@layer`|`@apply`' frontend/src --type-list 2>/dev/null || rg '`@layer`|`@apply`' frontend/src -g '*.css' -B2 -A2

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Look for any build output or CSS to see if h-120 compiles to valid CSS
find frontend -name "dist" -o -name "build" -o -name ".astro" | head -5

Repository: biko2/artemis-blog

Length of output: 44


🏁 Script executed:

# Check the package.json to understand the build setup better
cat frontend/package.json | grep -A20 '"dependencies"' | head -30

Repository: biko2/artemis-blog

Length of output: 615


Remove or replace the invalid h-120 class.

The h-120 class is not part of Tailwind CSS v4's default scale (which maxes out at h-96 = 384px) and has no custom theme extension defined in the project. This class likely won't generate valid CSS. Either use a standard height class (e.g., h-96) or add the missing theme configuration. The max-h-80 container (320px) suggests the image should fit within that constraint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/organisms/HeaderPost.astro` around lines 18 - 25, The
img element in HeaderPost.astro uses the h-120 class which is not a valid
Tailwind CSS v4 class (the default scale maxes out at h-96). Replace the h-120
class with a valid Tailwind height class such as h-96, or add custom height
configuration to the project's Tailwind theme if a specific height beyond the
default scale is required. Given the max-h-80 constraint on the parent figure
element, a standard height class like h-96 would be appropriate.

Comment on lines +10 to +12
export async function getStaticPaths() {
const res = await fetch(import.meta.env.PUBLIC_API_BASE + "/api/v1/posts");
const posts: Post[] = await res.json();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add error handling for the fetch operation.

The fetch call and .json() parsing have no error handling. If the API is unavailable or returns invalid JSON during build, the build will fail with an unclear error message.

🛡️ Proposed fix with error handling
 export async function getStaticPaths() {
-  const res = await fetch(import.meta.env.PUBLIC_API_BASE + "/api/v1/posts");
-  const posts: Post[] = await res.json();
+  const res = await fetch(import.meta.env.PUBLIC_API_BASE + "/api/v1/posts");
+  if (!res.ok) {
+    throw new Error(`Failed to fetch posts: ${res.status} ${res.statusText}`);
+  }
+  const posts: Post[] = await res.json();
   return posts.map((post: Post) => ({
     params: { slug: post.slug },
     props: { post, allPosts: posts },
   }));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function getStaticPaths() {
const res = await fetch(import.meta.env.PUBLIC_API_BASE + "/api/v1/posts");
const posts: Post[] = await res.json();
export async function getStaticPaths() {
const res = await fetch(import.meta.env.PUBLIC_API_BASE + "/api/v1/posts");
if (!res.ok) {
throw new Error(`Failed to fetch posts: ${res.status} ${res.statusText}`);
}
const posts: Post[] = await res.json();
return posts.map((post: Post) => ({
params: { slug: post.slug },
props: { post, allPosts: posts },
}));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/posts/`[slug].astro around lines 10 - 12, The fetch call
and .json() parsing in the getStaticPaths function have no error handling, which
means API failures or invalid JSON responses during build will result in unclear
error messages. Wrap both the fetch operation and the .json() parsing in a
try-catch block to catch any network errors or JSON parsing errors, and throw or
log a descriptive error message that includes details about the failure (such as
the HTTP status, network error, or parsing error). This will make it clear to
developers whether the API is unavailable, returns an error status, or returns
invalid JSON.

Comment on lines +44 to +47
set:html={"<spam class='font-bold'>" +
post.excerpt +
"</spam>" +
post.content}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix HTML typo and address XSS vulnerability.

Two critical issues:

  1. <spam> should be <span> (line 44)
  2. Using set:html with post.content is an XSS vulnerability if the content is not sanitized server-side
🐛 Proposed fix for the typo
       <section
         class="content leading-relaxed text-(--color-text)"
-        set:html={"<spam class='font-bold'>" +
+        set:html={"<span class='font-bold'>" +
           post.excerpt +
-          "</spam>" +
+          "</span>" +
           post.content}
       />

Note: Ensure post.content is sanitized server-side to prevent XSS attacks. If the API returns raw HTML from untrusted sources, consider using a sanitization library or rendering Markdown safely instead.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
set:html={"<spam class='font-bold'>" +
post.excerpt +
"</spam>" +
post.content}
set:html={"<span class='font-bold'>" +
post.excerpt +
"</span>" +
post.content}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/posts/`[slug].astro around lines 44 - 47, Fix the HTML tag
typo by replacing the opening tag from <spam> to <span> in the set:html template
expression. Additionally, address the XSS vulnerability by ensuring that
post.content is sanitized server-side before being rendered with set:html. If
the content comes from untrusted sources, implement server-side sanitization
using a library or safely render Markdown instead of directly injecting raw HTML
content.

}

@utility caption {
font-size: clamp(var(--label), 0.9rem+1w, var(--caption));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix invalid CSS unit and spacing in clamp function.

The caption utility has multiple syntax errors:

  • 1w is not a valid CSS unit (should be 1vw)
  • Missing spaces around the + operator
🐛 Proposed fix
 `@utility` caption {
-  font-size: clamp(var(--label), 0.9rem+1w, var(--caption));
+  font-size: clamp(var(--label), 0.9rem + 1vw, var(--caption));
   font-weight: 400;
   font-style: normal;
   line-height: 1.4;
🧰 Tools
🪛 Stylelint (17.12.0)

[error] 54-54: Expected single space after "+" operator (function-calc-no-unspaced-operator)

(function-calc-no-unspaced-operator)


[error] 54-54: Expected single space before "+" operator (function-calc-no-unspaced-operator)

(function-calc-no-unspaced-operator)


[error] 54-54: Unknown unit "w" (unit-no-unknown)

(unit-no-unknown)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/styles/global.css` at line 54, The clamp function in the caption
utility CSS rule contains two syntax errors that need to be corrected. First,
change the invalid CSS unit `1w` to the valid viewport width unit `1vw`. Second,
add spaces around the `+` operator in the middle parameter to match proper CSS
syntax formatting (changing `0.9rem+1w` to `0.9rem + 1vw`). The corrected clamp
function should have proper spacing and valid units throughout.

Source: Linters/SAST tools

export async function getPosts(): Promise<Post[]> {
const PUBLIC_API_BASE =
import.meta.env.PUBLIC_API_BASE ?? "http://localhost:3000";
const res = await fetch(`${PUBLIC_API_BASE}/api/v1/posts`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Add timeout to prevent indefinite build hangs.

The fetch call has no timeout. If the API is slow or unreachable, the build will hang indefinitely.

⏱️ Proposed fix with timeout
-  const res = await fetch(`${PUBLIC_API_BASE}/api/v1/posts`);
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 10000);
+  try {
+    const res = await fetch(`${PUBLIC_API_BASE}/api/v1/posts`, {
+      signal: controller.signal,
+    });
+    clearTimeout(timeoutId);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/utils/posts.ts` at line 6, The fetch call in the posts.ts file
at the PUBLIC_API_BASE endpoint has no timeout mechanism, which can cause the
build to hang indefinitely if the API is slow or unreachable. Add a timeout by
creating an AbortController, setting a timeout (e.g., 5-10 seconds) that calls
abort() on the controller, and passing the signal from the controller in the
fetch options as the second parameter. This ensures the request will be
cancelled if it exceeds the timeout duration.

Comment on lines +7 to +8
if (!res.ok) throw new Error(`Error al obtener los posts: ${res.status}`);
return res.json();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate API response shape to prevent cryptic build failures.

The function assumes the API returns valid Post[] but doesn't validate the response. If the API returns an error object or malformed data, downstream components will fail with unclear error messages during build.

🛡️ Proposed fix with validation
   if (!res.ok) throw new Error(`Error al obtener los posts: ${res.status}`);
-  return res.json();
+  const data = await res.json();
+  if (!Array.isArray(data)) {
+    throw new Error(`Expected array of posts, got: ${typeof data}`);
+  }
+  return data;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!res.ok) throw new Error(`Error al obtener los posts: ${res.status}`);
return res.json();
if (!res.ok) throw new Error(`Error al obtener los posts: ${res.status}`);
const data = await res.json();
if (!Array.isArray(data)) {
throw new Error(`Expected array of posts, got: ${typeof data}`);
}
return data;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/utils/posts.ts` around lines 7 - 8, Add response shape
validation to the function that fetches posts. After calling res.json(),
validate that the returned data is actually an array of Post objects with the
expected structure and properties. If the response data doesn't match the
expected Post[] shape, throw a descriptive error that clearly indicates the API
returned unexpected data format. This validation should occur before returning
the response to prevent downstream components from receiving malformed data and
failing with cryptic errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants