Release v1.0.12#451
Conversation
(SP: 1) [SHOP] make cart provider capability env reads runtime-safe
* feat(categories): add php/laravel/csharp/dotnet styles and refactor category styles via registry * fix(types): preserve category registry slug literals and normalize stats k-format
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📝 WalkthroughWalkthroughRelease v1.0.12 introduces a registry-driven category system for centralized styling/icon management, refactors environment variable resolution for shop cart capabilities to be runtime-safe on Netlify, removes all Sanity CMS studio infrastructure, updates blog management to in-house admin panel, adjusts LinkedIn follower display formatting, and bumps package version. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
frontend/data/category.ts (1)
13-15: Sort bydisplayOrderwhen derivingcategoryData.Right now the exported array depends on registry insertion order. Since each item carries
displayOrder, sort the derived data explicitly to prevent future ordering drift.♻️ Proposed refactor
-export const categoryData = categoryRegistry.map(item => - createCategory(item.slug, item.title, item.displayOrder) -); +export const categoryData = [...categoryRegistry] + .sort((a, b) => a.displayOrder - b.displayOrder) + .map(item => createCategory(item.slug, item.title, item.displayOrder));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/data/category.ts` around lines 13 - 15, categoryData currently maps categoryRegistry in insertion order, causing output to drift; before mapping call createCategory, sort the registry by each item's displayOrder to ensure deterministic ordering. Locate categoryData and replace the direct map over categoryRegistry with a sorted iteration (e.g., copy and sort categoryRegistry by item.displayOrder) and then map to createCategory(item.slug, item.title, item.displayOrder).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/app/`[locale]/shop/cart/capabilities.ts:
- Around line 25-40: Both resolveMonobankCheckoutEnabled and
resolveMonobankGooglePayEnabled can throw when calling readServerEnv (and thus
reintroduce SSR/runtime crashes); move any readServerEnv and isFlagEnabled calls
into the same fail-closed try/catch boundary so exceptions are caught and the
function returns false. Concretely, in resolveMonobankCheckoutEnabled wrap the
paymentsEnabled read and check (readServerEnv('PAYMENTS_ENABLED') +
isFlagEnabled) inside the try/catch that also calls isMonobankEnabled and return
false on any exception; do the same in resolveMonobankGooglePayEnabled so the
SHOP_MONOBANK_GPAY_ENABLED read is performed inside a try/catch and returns
false on error, referencing resolveMonobankCheckoutEnabled,
resolveMonobankGooglePayEnabled, isFlagEnabled, readServerEnv, and
isMonobankEnabled to locate the changes.
In `@frontend/lib/about/stats.ts`:
- Around line 16-18: The compact-thousands formatting branch that computes value
= n / 1000 and returns (Number.isInteger(value) ? value.toString() :
value.toFixed(1)) + 'k+' can produce strings like "2.0k+"; update the return to
strip a trailing ".0" after formatting (e.g., generate the formatted string
using toFixed(1) when needed, then remove a trailing ".0" via a replace or
similar) so the final result becomes "2k+" instead of "2.0k+" while still
preserving one decimal for non-round values.
In `@frontend/lib/env/stripe.ts`:
- Around line 37-42: The mode inference currently uses a raw NODE_ENV check;
change the logic that computes the mode (the const mode using rawMode and
nodeEnv) to reuse the existing isProductionLikeRuntime() helper so a
production-like runtime (e.g., APP_ENV=production) yields 'live' when
STRIPE_MODE is missing; update the other location that makes the same decision
(the later defaulting logic that currently checks nodeEnv) to also call
isProductionLikeRuntime(); ensure rawMode still accepts 'test'|'live' values
first, otherwise return 'live' when isProductionLikeRuntime() is true and 'test'
otherwise.
In `@frontend/lib/tests/shop/public-cart-env-contract.test.ts`:
- Around line 173-183: The test "enables stripe capability from runtime-only env
when config is valid" uses mock Stripe secret-shaped strings which trigger
secret scanners; update the readServerEnvMock implementation used in that test
(and the nearby case keys 'PAYMENTS_ENABLED', 'STRIPE_SECRET_KEY',
'STRIPE_WEBHOOK_SECRET', 'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY') to construct
scanner-safe fixtures from non-secret fragments (e.g., join or concat innocuous
pieces like 'sk_test', 'runtime_only', '123456' or use clearly synthetic names
like 'stripe_runtime_key_1') so the values preserve test behavior but no longer
resemble real secret literals. Ensure the change is applied to the same mock in
lines around that test (cases for the four keys).
In `@README.md`:
- Around line 84-90: Remove the empty blockquote line inside the TIP admonition
and insert an HTML separator between the two admonitions so markdownlint MD028
is not triggered; specifically, edit the README.md section containing the
"[!IMPORTANT]" and "[!TIP]" blocks to delete the blank ">" line inside the TIP
block and add a separator (for example an HTML comment or <hr/>) between the
"[!IMPORTANT]" and "[!TIP]" callouts to prevent adjacent blockquote lint
failures.
- Around line 3-7: Inside the [!NOTE] block in the README replace the nested H3
"### Train smarter, interview stronger." with plain bold text (e.g., **Train
smarter, interview stronger.**) so the H1 is not immediately followed by a
lower-level heading; update the line containing "### Train smarter, interview
stronger." accordingly to remove the leading "###" and use bold formatting
instead.
---
Nitpick comments:
In `@frontend/data/category.ts`:
- Around line 13-15: categoryData currently maps categoryRegistry in insertion
order, causing output to drift; before mapping call createCategory, sort the
registry by each item's displayOrder to ensure deterministic ordering. Locate
categoryData and replace the direct map over categoryRegistry with a sorted
iteration (e.g., copy and sort categoryRegistry by item.displayOrder) and then
map to createCategory(item.slug, item.title, item.displayOrder).
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: febd5bd8-f975-47b8-8847-100fc3771600
⛔ Files ignored due to path filters (15)
assets/01-screencapture.pngis excluded by!**/*.pngassets/02-screencapture.pngis excluded by!**/*.pngassets/03-screencapture.pngis excluded by!**/*.pngassets/04-screencapture.pngis excluded by!**/*.pngassets/05-screencapture.pngis excluded by!**/*.pngassets/06-screencapture.pngis excluded by!**/*.pngassets/07-screencapture.pngis excluded by!**/*.pngassets/08-screencapture.pngis excluded by!**/*.pngassets/09-screencapture.pngis excluded by!**/*.pngfrontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/public/icons/csharp.svgis excluded by!**/*.svgfrontend/public/icons/dotnet.svgis excluded by!**/*.svgfrontend/public/icons/laravel.svgis excluded by!**/*.svgfrontend/public/icons/php.svgis excluded by!**/*.svgstudio/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (27)
CHANGELOG.mdREADME.mdfrontend/app/[locale]/shop/cart/capabilities.tsfrontend/components/about/HeroSection.tsxfrontend/data/category.tsfrontend/data/categoryRegistry.tsfrontend/data/categoryStyles.tsfrontend/lib/about/stats.tsfrontend/lib/env/monobank.tsfrontend/lib/env/provider-runtime.tsfrontend/lib/env/stripe.tsfrontend/lib/tests/shop/public-cart-env-contract.test.tsfrontend/package.jsonstudio/.gitignorestudio/.prettierrcstudio/eslint.config.mjsstudio/package.jsonstudio/sanity.cli.tsstudio/sanity.config.tsstudio/schemaTypes/author.tsstudio/schemaTypes/blockContent.tsstudio/schemaTypes/category.tsstudio/schemaTypes/index.tsstudio/schemaTypes/post.tsstudio/schemaTypes/socialLink.tsstudio/static/.gitkeepstudio/tsconfig.json
💤 Files with no reviewable changes (14)
- studio/static/.gitkeep
- studio/eslint.config.mjs
- studio/schemaTypes/blockContent.ts
- studio/package.json
- studio/tsconfig.json
- studio/.prettierrc
- studio/.gitignore
- studio/schemaTypes/author.ts
- studio/schemaTypes/category.ts
- studio/schemaTypes/index.ts
- studio/schemaTypes/socialLink.ts
- studio/sanity.cli.ts
- studio/schemaTypes/post.ts
- studio/sanity.config.ts
| export function resolveMonobankCheckoutEnabled(): boolean { | ||
| return resolveStandardStorefrontProviderCapabilities().monobankCheckoutEnabled; | ||
| const paymentsEnabled = isFlagEnabled(readServerEnv('PAYMENTS_ENABLED')); | ||
| if (!paymentsEnabled) return false; | ||
|
|
||
| try { | ||
| return isMonobankEnabled(); | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| export function resolveMonobankGooglePayEnabled(): boolean { | ||
| return resolveStandardStorefrontProviderCapabilities() | ||
| .monobankGooglePayEnabled; | ||
| if (!resolveMonobankCheckoutEnabled()) return false; | ||
|
|
||
| return isFlagEnabled(readServerEnv('SHOP_MONOBANK_GPAY_ENABLED')); | ||
| } |
There was a problem hiding this comment.
Keep Monobank env reads inside the fail-closed boundary.
Line 26 and Line 39 can still throw before returning false, which can reintroduce the cart SSR/runtime crash path this change is trying to prevent.
🛡️ Proposed fail-closed adjustment
export function resolveMonobankCheckoutEnabled(): boolean {
- const paymentsEnabled = isFlagEnabled(readServerEnv('PAYMENTS_ENABLED'));
- if (!paymentsEnabled) return false;
-
try {
+ const paymentsEnabled = isFlagEnabled(readServerEnv('PAYMENTS_ENABLED'));
+ if (!paymentsEnabled) return false;
+
return isMonobankEnabled();
} catch {
return false;
}
}
export function resolveMonobankGooglePayEnabled(): boolean {
- if (!resolveMonobankCheckoutEnabled()) return false;
-
- return isFlagEnabled(readServerEnv('SHOP_MONOBANK_GPAY_ENABLED'));
+ try {
+ if (!resolveMonobankCheckoutEnabled()) return false;
+
+ return isFlagEnabled(readServerEnv('SHOP_MONOBANK_GPAY_ENABLED'));
+ } catch {
+ return false;
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/app/`[locale]/shop/cart/capabilities.ts around lines 25 - 40, Both
resolveMonobankCheckoutEnabled and resolveMonobankGooglePayEnabled can throw
when calling readServerEnv (and thus reintroduce SSR/runtime crashes); move any
readServerEnv and isFlagEnabled calls into the same fail-closed try/catch
boundary so exceptions are caught and the function returns false. Concretely, in
resolveMonobankCheckoutEnabled wrap the paymentsEnabled read and check
(readServerEnv('PAYMENTS_ENABLED') + isFlagEnabled) inside the try/catch that
also calls isMonobankEnabled and return false on any exception; do the same in
resolveMonobankGooglePayEnabled so the SHOP_MONOBANK_GPAY_ENABLED read is
performed inside a try/catch and returns false on error, referencing
resolveMonobankCheckoutEnabled, resolveMonobankGooglePayEnabled, isFlagEnabled,
readServerEnv, and isMonobankEnabled to locate the changes.
| if (n >= 1000) { | ||
| const value = n / 1000; | ||
| return (Number.isInteger(value) ? value.toString() : value.toFixed(1)) + 'k+'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify compact formatting around thousand boundaries without running repository code.
node - <<'NODE'
for (const n of [1000, 1500, 1999, 2000]) {
const value = n / 1000;
const current = (Number.isInteger(value) ? value.toString() : value.toFixed(1)) + 'k+';
const fixed = (Number.isInteger(value) ? value.toString() : value.toFixed(1).replace(/\.0$/, '')) + 'k+';
console.log(`${n}: current=${current}, fixed=${fixed}`);
}
NODERepository: DevLoversTeam/devlovers.net
Length of output: 192
🏁 Script executed:
cat -n frontend/lib/about/stats.ts | sed -n '14,20p'Repository: DevLoversTeam/devlovers.net
Length of output: 315
Strip rounded .0 from compact metric values.
Line 18 produces 2.0k+ for values like 1999 because toFixed(1) rounds 1.999 to 2.0. Strip the trailing .0 after formatting to avoid this visual artifact.
🔧 Proposed fix
if (n >= 1000) {
const value = n / 1000;
- return (Number.isInteger(value) ? value.toString() : value.toFixed(1)) + 'k+';
+ const formatted = Number.isInteger(value)
+ ? value.toString()
+ : value.toFixed(1).replace(/\.0$/, '');
+ return formatted + 'k+';
}📝 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.
| if (n >= 1000) { | |
| const value = n / 1000; | |
| return (Number.isInteger(value) ? value.toString() : value.toFixed(1)) + 'k+'; | |
| if (n >= 1000) { | |
| const value = n / 1000; | |
| const formatted = Number.isInteger(value) | |
| ? value.toString() | |
| : value.toFixed(1).replace(/\.0$/, ''); | |
| return formatted + 'k+'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/lib/about/stats.ts` around lines 16 - 18, The compact-thousands
formatting branch that computes value = n / 1000 and returns
(Number.isInteger(value) ? value.toString() : value.toFixed(1)) + 'k+' can
produce strings like "2.0k+"; update the return to strip a trailing ".0" after
formatting (e.g., generate the formatted string using toFixed(1) when needed,
then remove a trailing ".0" via a replace or similar) so the final result
becomes "2k+" instead of "2.0k+" while still preserving one decimal for
non-round values.
| const mode = | ||
| (nonEmpty(process.env.STRIPE_MODE) as 'test' | 'live' | null) ?? | ||
| (runtimeEnv.NODE_ENV === 'production' ? 'live' : 'test'); | ||
| rawMode === 'test' || rawMode === 'live' | ||
| ? rawMode | ||
| : String(nodeEnv).trim().toLowerCase() === 'production' | ||
| ? 'live' | ||
| : 'test'; |
There was a problem hiding this comment.
Use the same production-like signal when inferring Stripe mode.
Line 40 only checks NODE_ENV, but Line 57 treats APP_ENV=production as production-like. In that case, a missing STRIPE_MODE defaults to test and then rejects checkout. Reuse isProductionLikeRuntime() for the default mode decision.
🔧 Proposed fix
+ const productionLike = isProductionLikeRuntime();
const mode =
rawMode === 'test' || rawMode === 'live'
? rawMode
- : String(nodeEnv).trim().toLowerCase() === 'production'
+ : productionLike || String(nodeEnv).trim().toLowerCase() === 'production'
? 'live'
: 'test';
const paymentsEnabled =
paymentsFlag === 'true' && !!secretKey && !!webhookSecret;
@@
- if (isProductionLikeRuntime() && mode !== 'live') {
+ if (productionLike && mode !== 'live') {Also applies to: 57-57
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/lib/env/stripe.ts` around lines 37 - 42, The mode inference
currently uses a raw NODE_ENV check; change the logic that computes the mode
(the const mode using rawMode and nodeEnv) to reuse the existing
isProductionLikeRuntime() helper so a production-like runtime (e.g.,
APP_ENV=production) yields 'live' when STRIPE_MODE is missing; update the other
location that makes the same decision (the later defaulting logic that currently
checks nodeEnv) to also call isProductionLikeRuntime(); ensure rawMode still
accepts 'test'|'live' values first, otherwise return 'live' when
isProductionLikeRuntime() is true and 'test' otherwise.
| it('enables stripe capability from runtime-only env when config is valid', async () => { | ||
| readServerEnvMock.mockImplementation((key: string) => { | ||
| switch (key) { | ||
| case 'PAYMENTS_ENABLED': | ||
| return 'true'; | ||
| case 'STRIPE_SECRET_KEY': | ||
| return 'sk_test_runtime_only_1234567890'; | ||
| case 'STRIPE_WEBHOOK_SECRET': | ||
| return 'whsec_runtime_only_1234567890'; | ||
| case 'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY': | ||
| return 'pk_test_runtime_only_1234567890'; |
There was a problem hiding this comment.
Avoid secret-shaped Stripe fixtures in source.
Line 226 is already flagged by secret scanning. These are fake values, but constructing them from parts avoids recurring scanner noise while preserving runtime test behavior.
🔐 Proposed scanner-safe fixture construction
+const stripeFixture = (...parts: string[]) => parts.join('_');
+
it('enables stripe capability from runtime-only env when config is valid', async () => {
readServerEnvMock.mockImplementation((key: string) => {
switch (key) {
case 'PAYMENTS_ENABLED':
return 'true';
case 'STRIPE_SECRET_KEY':
- return 'sk_test_runtime_only_1234567890';
+ return stripeFixture('sk', 'test', 'runtime', 'only', '1234567890');
case 'STRIPE_WEBHOOK_SECRET':
- return 'whsec_runtime_only_1234567890';
+ return stripeFixture('whsec', 'runtime', 'only', '1234567890');
case 'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY':
- return 'pk_test_runtime_only_1234567890';
+ return stripeFixture('pk', 'test', 'runtime', 'only', '1234567890');
default:
return baselineCartEnv(key);
}
@@
case 'PAYMENTS_ENABLED':
return 'true';
case 'STRIPE_SECRET_KEY':
- return 'sk_test_placeholder';
+ return stripeFixture('sk', 'test', 'placeholder');
case 'STRIPE_WEBHOOK_SECRET':
- return 'whsec_placeholder_value';
+ return stripeFixture('whsec', 'placeholder', 'value');
case 'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY':
- return 'pk_test_placeholder';
+ return stripeFixture('pk', 'test', 'placeholder');Also applies to: 218-230
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/lib/tests/shop/public-cart-env-contract.test.ts` around lines 173 -
183, The test "enables stripe capability from runtime-only env when config is
valid" uses mock Stripe secret-shaped strings which trigger secret scanners;
update the readServerEnvMock implementation used in that test (and the nearby
case keys 'PAYMENTS_ENABLED', 'STRIPE_SECRET_KEY', 'STRIPE_WEBHOOK_SECRET',
'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY') to construct scanner-safe fixtures from
non-secret fragments (e.g., join or concat innocuous pieces like 'sk_test',
'runtime_only', '123456' or use clearly synthetic names like
'stripe_runtime_key_1') so the values preserve test behavior but no longer
resemble real secret literals. Ensure the change is applied to the same mock in
lines around that test (cases for the four keys).
| > [!NOTE] | ||
| > | ||
| > ### Train smarter, interview stronger. | ||
| > | ||
| > DevLovers is a multi-language interview prep platform with curated Q&A, timed quizzes, leaderboard rankings, a developer blog, and a personal dashboard to track real learning progress. |
There was a problem hiding this comment.
Fix the skipped heading level in the note.
### immediately after the H1 triggers markdownlint MD001. This can be bold text instead of a nested heading.
📝 Proposed fix
> [!NOTE]
>
-> ### Train smarter, interview stronger.
+> **Train smarter, interview stronger.**
>
> DevLovers is a multi-language interview prep platform with curated Q&A, timed quizzes, leaderboard rankings, a developer blog, and a personal dashboard to track real learning progress.📝 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.
| > [!NOTE] | |
| > | |
| > ### Train smarter, interview stronger. | |
| > | |
| > DevLovers is a multi-language interview prep platform with curated Q&A, timed quizzes, leaderboard rankings, a developer blog, and a personal dashboard to track real learning progress. | |
| > [!NOTE] | |
| > | |
| > **Train smarter, interview stronger.** | |
| > | |
| > DevLovers is a multi-language interview prep platform with curated Q&A, timed quizzes, leaderboard rankings, a developer blog, and a personal dashboard to track real learning progress. |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 5-5: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 3 - 7, Inside the [!NOTE] block in the README replace
the nested H3 "### Train smarter, interview stronger." with plain bold text
(e.g., **Train smarter, interview stronger.**) so the H1 is not immediately
followed by a lower-level heading; update the line containing "### Train
smarter, interview stronger." accordingly to remove the leading "###" and use
bold formatting instead.
| > [!IMPORTANT] | ||
| > **Contact me:** [contact@devlovers.net](mailto:contact@devlovers.net) | ||
|
|
||
| **Contact me:** [contact@devlovers.net](mailto:contact@devlovers.net) | ||
| > [!TIP] | ||
| > **Production:** [devlovers.net](https://devlovers.net) | ||
| > | ||
| > **Develop:** [develop.devlovers.net](https://develop-devlovers.netlify.app) [](https://app.netlify.com/projects/develop-devlovers/deploys) |
There was a problem hiding this comment.
Avoid adjacent blockquote lint failures.
The back-to-back admonitions and blank quoted line can trigger markdownlint MD028. Add an HTML separator between callouts and remove the blank blockquote line inside the TIP.
📝 Proposed fix
> [!IMPORTANT]
> **Contact me:** [contact@devlovers.net](mailto:contact@devlovers.net)
+<!-- -->
+
> [!TIP]
> **Production:** [devlovers.net](https://devlovers.net)
->
> **Develop:** [develop.devlovers.net](https://develop-devlovers.netlify.app) [](https://app.netlify.com/projects/develop-devlovers/deploys)📝 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.
| > [!IMPORTANT] | |
| > **Contact me:** [contact@devlovers.net](mailto:contact@devlovers.net) | |
| **Contact me:** [contact@devlovers.net](mailto:contact@devlovers.net) | |
| > [!TIP] | |
| > **Production:** [devlovers.net](https://devlovers.net) | |
| > | |
| > **Develop:** [develop.devlovers.net](https://develop-devlovers.netlify.app) [](https://app.netlify.com/projects/develop-devlovers/deploys) | |
| > [!IMPORTANT] | |
| > **Contact me:** [contact@devlovers.net](mailto:contact@devlovers.net) | |
| <!-- --> | |
| > [!TIP] | |
| > **Production:** [devlovers.net](https://devlovers.net) | |
| > **Develop:** [develop.devlovers.net](https://develop-devlovers.netlify.app) [](https://app.netlify.com/projects/develop-devlovers/deploys) |
🧰 Tools
🪛 LanguageTool
[style] ~87-~87: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 1575 characters long)
Context: ....net](mailto:contact@devlovers.net) > [!TIP] > Production: [devlovers.net](h...
(EN_EXCESSIVE_EXCLAMATION)
🪛 markdownlint-cli2 (0.22.0)
[warning] 86-86: Blank line inside blockquote
(MD028, no-blanks-blockquote)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 84 - 90, Remove the empty blockquote line inside the
TIP admonition and insert an HTML separator between the two admonitions so
markdownlint MD028 is not triggered; specifically, edit the README.md section
containing the "[!IMPORTANT]" and "[!TIP]" blocks to delete the blank ">" line
inside the TIP block and add a separator (for example an HTML comment or <hr/>)
between the "[!IMPORTANT]" and "[!TIP]" callouts to prevent adjacent blockquote
lint failures.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation