Skip to content

Conversation

@anshgoyalevil
Copy link
Member

@anshgoyalevil anshgoyalevil commented Mar 23, 2025

Description

Related issue(s)
fixes #3777

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Integrated automated locale consistency checks into the pull request workflow, which now flags and provides feedback on internationalization issues.
    • Added a new command to validate language assets, ensuring uniform localized content across all supported languages.
    • Introduced a mechanism to comment on pull requests with detected locale issues and to remove comments when no issues are found.
  • Tests

    • Implemented comprehensive test coverage for locale validation, enhancing reliability and early detection of discrepancies.

Signed-off-by: Ansh Goyal <[email protected]>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 23, 2025

Walkthrough

The pull request introduces locale validation into the project. It adds a new GitHub Actions job that executes locale checks during Node.js pull requests. The workflow runs a new script that verifies JSON locale files for consistent keys across languages. If inconsistencies are found, the workflow posts a comment on the pull request; if not, it removes any existing comment. Additionally, a new script in package.json and comprehensive tests have been added to support and validate the locale checking functionality.

Changes

File(s) Change Summary
.github/.../if-nodejs-pr-testing.yml Added new steps: "Run locale checks" that executes npm run test:locales, "Comment on PR with locale issues", and "Delete locale check comment if no issues".
package.json Added a new script entry "test:locales": "tsx scripts/utils/check-locales.ts".
scripts/.../check-locales.ts Introduced functions to extract JSON keys, read JSON files, and validate keys across locales with error handling and process exit codes.
tests/check-locales.test.js Added Jest tests covering locale extraction, file reading, and validation behavior for missing or inconsistent locale keys.

Sequence Diagram(s)

sequenceDiagram
    participant PR as "Pull Request"
    participant GA as "GitHub Actions"
    participant LS as "Locale Script"
    participant CM as "Comment Service"
    
    PR->>GA: Trigger Node.js PR workflow
    GA->>LS: Execute `npm run test:locales`
    LS-->>GA: Return validation result (errors or success)
    alt Locale issues detected
        GA->>CM: Post comment detailing locale issues
    else No locale issues
        GA->>CM: Delete existing locale issue comment (if any)
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement a script to check for missing/inconsistent locale variables (#3777)
Integrate locale check into the GitHub Action workflow (#3777)
Ensure compatibility with the existing locale structure (#3777)

Suggested labels

ready-to-merge

Suggested reviewers

  • derberg
  • magicmatatjahu
  • Mayaleeeee
  • asyncapi-bot-eve
  • sambhavgupta0705
  • akshatnema

Poem

Hoppity hop, I code with glee,
Locale checks now run swift and free.
JSON keys align in rhythmic beat,
Workflows leap, ensuring no defeat.
With every test, my heart does sing,
A rabbit’s cheer for code that’s king!
🐇💻🎉

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@netlify
Copy link

netlify bot commented Mar 23, 2025

Deploy Preview for asyncapi-website ready!

Name Link
🔨 Latest commit 0507f84
🔍 Latest deploy log https://app.netlify.com/sites/asyncapi-website/deploys/680a7e1001e8ec0007139b54
😎 Deploy Preview https://deploy-preview-3950--asyncapi-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@codecov
Copy link

codecov bot commented Mar 23, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (262159f) to head (0507f84).
Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #3950   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           21        22    +1     
  Lines          671       742   +71     
  Branches       115       123    +8     
=========================================
+ Hits           671       742   +71     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (6)
tests/check-locales.test.js (2)

31-39: Consolidate the double path mocking
You’re re-mocking path here, even though it was already mocked in lines 11–18. This could lead to confusion or unexpected behavior. Consider moving all path mocking logic into a single place or removing the duplicate mock.

- jest.mock('path', () => {
-   const originalPath = jest.requireActual('path');
-   return {
-     ...originalPath,
-     dirname: jest.fn().mockReturnValue('/mock/dir'),
-     resolve: jest.fn().mockReturnValue('/mock/path/locales'),
-     join: jest.fn().mockImplementation((...args) => args.join('/'))
-   };
- });
+ // Remove or refactor this block if the earlier path mock suffices

42-47: Restore original process.exit in afterEach
While spying on process.exit is helpful in tests, consider restoring it in an afterEach block to avoid side effects on subsequent tests.

beforeEach(() => {
  jest.clearAllMocks();
});

+ afterEach(() => {
+   mockExit.mockRestore();
+ });
🧰 Tools
🪛 ESLint

[error] 43-43: Delete ··

(prettier/prettier)

.github/workflows/if-nodejs-pr-testing.yml (1)

129-129: Remove trailing spaces in YAML
Static analysis flagged trailing spaces in lines 129, 146, 148, and 152. Consider removing them for cleaner formatting.

  set -e
- 
+ 
  # If the command failed, extract and format the error message
  ...
            ### Locale Check Results
-             
+ 
            We found issues with locale keys:
-             
+ 
            ```
-             
+ 
            Please make sure all locale files have the same translation keys.

Also applies to: 146-146, 148-148, 152-152

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 129-129: trailing spaces

(trailing-spaces)

scripts/utils/check-locales.ts (3)

1-2: Unify multiple path imports
You import both path and dirname separately from 'path'. Consider combining them to reduce duplication.

-import fs from 'fs';
-import path from 'path';
+import fs from 'fs';
+import path, { dirname } from 'path';
🧰 Tools
🪛 ESLint

[error] 1-6: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 2-2: 'path' imported multiple times.

(import/no-duplicates)


13-19: Remove extra trailing comma and reduce multiline
Static analysis indicates a stylistic preference regarding path.resolve. You can simplify to a single line without trailing commas.

-const localesDir = path.resolve(
-  currentDirPath,
-  '..',
-  '..',
-  'public',
-  'locales',
-);
+const localesDir = path.resolve(currentDirPath, '..', '..', 'public', 'locales');
🧰 Tools
🪛 ESLint

[error] 13-19: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 18-18: Unexpected trailing comma.

(comma-dangle)


121-121: Consider alternatives to continue
The continue statement is sometimes discouraged due to readability concerns. You could refactor this logic with an if condition to reduce jumping to the next iteration.

🧰 Tools
🪛 ESLint

[error] 121-121: Expected blank line before this statement.

(padding-line-between-statements)


[error] 121-121: Unexpected use of continue statement.

(no-continue)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16491f6 and 0dbd7f6.

📒 Files selected for processing (4)
  • .github/workflows/if-nodejs-pr-testing.yml (1 hunks)
  • package.json (1 hunks)
  • scripts/utils/check-locales.ts (1 hunks)
  • tests/check-locales.test.js (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
tests/check-locales.test.js (1)
scripts/utils/check-locales.ts (3)
  • extractKeys (170-170)
  • readJSONFilesInDir (170-170)
  • validateLocales (170-170)
🪛 ESLint
tests/check-locales.test.js

[error] 1-1: 'path' is assigned a value but never used.

(no-unused-vars)


[error] 28-28: Missing file extension "ts" for "../scripts/utils/check-locales"

(import/extensions)


[error] 29-29: Missing file extension "ts" for "../scripts/utils/logger"

(import/extensions)


[error] 43-43: Delete ··

(prettier/prettier)


[error] 57-57: Delete ····

(prettier/prettier)


[error] 64-64: Delete ····

(prettier/prettier)


[error] 66-66: Delete ····

(prettier/prettier)


[error] 69-69: Delete ,

(prettier/prettier)


[error] 74-74: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 76-76: Delete ····

(prettier/prettier)


[error] 78-78: Delete ····

(prettier/prettier)


[error] 82-82: Delete ····

(prettier/prettier)


[error] 85-85: Delete ····

(prettier/prettier)


[error] 87-87: Delete ····

(prettier/prettier)


[error] 89-89: Delete ····

(prettier/prettier)


[error] 95-95: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 97-97: Delete ····

(prettier/prettier)


[error] 99-99: Delete ····

(prettier/prettier)


[error] 101-101: Delete ····

(prettier/prettier)


[error] 105-105: Delete ····

(prettier/prettier)


[error] 108-108: Delete ····

(prettier/prettier)


[error] 112-114: Unnecessary 'else' after 'return'.

(no-else-return)


[error] 116-116: Delete ····

(prettier/prettier)


[error] 118-118: Delete ····

(prettier/prettier)


[error] 119-121: Replace ⏎······"❌·Language·'de'·is·missing·these·keys:·key2"⏎···· with "❌·Language·'de'·is·missing·these·keys:·key2"

(prettier/prettier)


[error] 127-127: Delete ····

(prettier/prettier)


[error] 131-131: Delete ····

(prettier/prettier)


[error] 133-133: Delete ····

(prettier/prettier)


[error] 134-137: Replace ⏎······'Error·validating·locales:',⏎······testError⏎···· with 'Error·validating·locales:',·testError

(prettier/prettier)


[error] 143-143: Delete ····

(prettier/prettier)


[error] 145-145: Delete ····

(prettier/prettier)


[error] 146-149: Replace ⏎······'No·language·directories·found·in',⏎······'/mock/path/locales'⏎···· with 'No·language·directories·found·in',·'/mock/path/locales'

(prettier/prettier)


[error] 156-156: Delete ····

(prettier/prettier)


[error] 161-161: Delete ····

(prettier/prettier)


[error] 163-163: Delete ····

(prettier/prettier)


[error] 164-167: Replace ⏎······expect.stringContaining(Error·reading),⏎······expect.any(String)⏎···· with expect.stringContaining(Error·reading),·expect.any(String)

(prettier/prettier)


[error] 173-173: Delete ····

(prettier/prettier)


[error] 177-177: Delete ····

(prettier/prettier)


[error] 179-179: Delete ····

(prettier/prettier)


[error] 180-183: Replace ⏎······'Error·reading·directory·/mock/dir:',⏎······'Directory·read·error'⏎···· with 'Error·reading·directory·/mock/dir:',·'Directory·read·error'

(prettier/prettier)


[error] 184-184: Delete ····

(prettier/prettier)


[error] 187-187: Delete ··

(prettier/prettier)


[error] 190-190: Expected an error object to be thrown.

(no-throw-literal)


[error] 192-192: Delete ····

(prettier/prettier)


[error] 194-194: Delete ····

(prettier/prettier)


[error] 201-201: Delete ····

(prettier/prettier)


[error] 204-204: Delete ····

(prettier/prettier)


[error] 207-207: Expected an error object to be thrown.

(no-throw-literal)


[error] 209-209: Delete ····

(prettier/prettier)


[error] 217-217: Delete ··

(prettier/prettier)


[error] 220-220: Delete ····

(prettier/prettier)


[error] 222-222: Delete ····

(prettier/prettier)


[error] 226-226: Delete ····

(prettier/prettier)


[error] 229-229: Delete ····

(prettier/prettier)


[error] 231-231: Delete ····

(prettier/prettier)


[error] 233-233: Delete ····

(prettier/prettier)


[error] 236-236: Insert

(prettier/prettier)

scripts/utils/check-locales.ts

[error] 1-6: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 2-2: 'path' imported multiple times.

(import/no-duplicates)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


[error] 6-6: path import should occur before import of ../utils/logger

(import/order)


[error] 6-6: 'path' imported multiple times.

(import/no-duplicates)


[error] 13-19: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 18-18: Unexpected trailing comma.

(comma-dangle)


[error] 28-41: Expected blank line after variable declarations.

(newline-after-var)


[error] 28-31: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 29-29: 'obj' is already declared in the upper scope on line 27 column 22.

(@typescript-eslint/no-shadow)


[error] 30-30: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Expected blank line after variable declarations.

(newline-after-var)


[error] 35-37: Expected blank line before this statement.

(padding-line-between-statements)


[error] 38-38: Expected blank line before this statement.

(padding-line-between-statements)


[error] 39-39: Delete ,

(prettier/prettier)


[error] 39-39: Unexpected trailing comma.

(comma-dangle)


[error] 42-42: Expected blank line before this statement.

(padding-line-between-statements)


[error] 53-53: Expected blank line after variable declarations.

(newline-after-var)


[error] 54-69: Expected blank line before this statement.

(padding-line-between-statements)


[error] 58-58: Expected blank line after variable declarations.

(newline-after-var)


[error] 59-67: Expected blank line before this statement.

(padding-line-between-statements)


[error] 60-60: Expected blank line after variable declarations.

(newline-after-var)


[error] 61-61: Expected blank line before this statement.

(padding-line-between-statements)


[error] 66-66: Expected blank line before this statement.

(padding-line-between-statements)


[error] 68-68: Delete ,

(prettier/prettier)


[error] 68-68: Unexpected trailing comma.

(comma-dangle)


[error] 74-74: Expected blank line before this statement.

(padding-line-between-statements)


[error] 92-92: Expected blank line before this statement.

(padding-line-between-statements)


[error] 101-101: Expected blank line after variable declarations.

(newline-after-var)


[error] 102-102: Expected blank line before this statement.

(padding-line-between-statements)


[error] 104-107: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 106-106: Unexpected trailing comma.

(comma-dangle)


[error] 107-107: Unexpected trailing comma.

(comma-dangle)


[error] 111-113: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 112-112: Unexpected trailing comma.

(comma-dangle)


[error] 118-120: Expected blank line after variable declarations.

(newline-after-var)


[error] 118-120: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 119-119: Unexpected trailing comma.

(comma-dangle)


[error] 121-121: Expected blank line before this statement.

(padding-line-between-statements)


[error] 121-121: Unexpected use of continue statement.

(no-continue)


[error] 123-125: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 124-124: Unexpected trailing comma.

(comma-dangle)


[error] 130-132: Expected blank line after variable declarations.

(newline-after-var)


[error] 130-132: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 131-131: Unexpected trailing comma.

(comma-dangle)


[error] 133-133: Expected blank line before this statement.

(padding-line-between-statements)


[error] 134-134: Delete ,

(prettier/prettier)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


[error] 137-139: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 138-138: '_' is defined but never used.

(no-unused-vars)


[error] 138-138: Unexpected trailing comma.

(comma-dangle)


[error] 143-148: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 144-146: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 145-145: Unexpected trailing comma.

(comma-dangle)


[error] 152-161: Unexpected negated condition.

(no-negated-condition)


[error] 153-155: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 154-154: Unexpected trailing comma.

(comma-dangle)


[error] 157-159: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 158-158: Unexpected trailing comma.

(comma-dangle)


[error] 170-170: Run autofix to sort these exports!

(simple-import-sort/exports)

🪛 YAMLlint (1.35.1)
.github/workflows/if-nodejs-pr-testing.yml

[error] 129-129: trailing spaces

(trailing-spaces)


[error] 146-146: trailing spaces

(trailing-spaces)


[error] 148-148: trailing spaces

(trailing-spaces)


[error] 152-152: trailing spaces

(trailing-spaces)

🔇 Additional comments (4)
package.json (1)

29-29: Good addition for locale validation
This new script aligns perfectly with your new locale checking logic.

tests/check-locales.test.js (1)

1-236: Comprehensive test coverage
Overall, this suite provides thorough coverage for locale validation scenarios, including error handling and edge cases. Great job!

🧰 Tools
🪛 ESLint

[error] 1-1: 'path' is assigned a value but never used.

(no-unused-vars)


[error] 28-28: Missing file extension "ts" for "../scripts/utils/check-locales"

(import/extensions)


[error] 29-29: Missing file extension "ts" for "../scripts/utils/logger"

(import/extensions)


[error] 43-43: Delete ··

(prettier/prettier)


[error] 57-57: Delete ····

(prettier/prettier)


[error] 64-64: Delete ····

(prettier/prettier)


[error] 66-66: Delete ····

(prettier/prettier)


[error] 69-69: Delete ,

(prettier/prettier)


[error] 74-74: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 76-76: Delete ····

(prettier/prettier)


[error] 78-78: Delete ····

(prettier/prettier)


[error] 82-82: Delete ····

(prettier/prettier)


[error] 85-85: Delete ····

(prettier/prettier)


[error] 87-87: Delete ····

(prettier/prettier)


[error] 89-89: Delete ····

(prettier/prettier)


[error] 95-95: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 97-97: Delete ····

(prettier/prettier)


[error] 99-99: Delete ····

(prettier/prettier)


[error] 101-101: Delete ····

(prettier/prettier)


[error] 105-105: Delete ····

(prettier/prettier)


[error] 108-108: Delete ····

(prettier/prettier)


[error] 112-114: Unnecessary 'else' after 'return'.

(no-else-return)


[error] 116-116: Delete ····

(prettier/prettier)


[error] 118-118: Delete ····

(prettier/prettier)


[error] 119-121: Replace ⏎······"❌·Language·'de'·is·missing·these·keys:·key2"⏎···· with "❌·Language·'de'·is·missing·these·keys:·key2"

(prettier/prettier)


[error] 127-127: Delete ····

(prettier/prettier)


[error] 131-131: Delete ····

(prettier/prettier)


[error] 133-133: Delete ····

(prettier/prettier)


[error] 134-137: Replace ⏎······'Error·validating·locales:',⏎······testError⏎···· with 'Error·validating·locales:',·testError

(prettier/prettier)


[error] 143-143: Delete ····

(prettier/prettier)


[error] 145-145: Delete ····

(prettier/prettier)


[error] 146-149: Replace ⏎······'No·language·directories·found·in',⏎······'/mock/path/locales'⏎···· with 'No·language·directories·found·in',·'/mock/path/locales'

(prettier/prettier)


[error] 156-156: Delete ····

(prettier/prettier)


[error] 161-161: Delete ····

(prettier/prettier)


[error] 163-163: Delete ····

(prettier/prettier)


[error] 164-167: Replace ⏎······expect.stringContaining(Error·reading),⏎······expect.any(String)⏎···· with expect.stringContaining(Error·reading),·expect.any(String)

(prettier/prettier)


[error] 173-173: Delete ····

(prettier/prettier)


[error] 177-177: Delete ····

(prettier/prettier)


[error] 179-179: Delete ····

(prettier/prettier)


[error] 180-183: Replace ⏎······'Error·reading·directory·/mock/dir:',⏎······'Directory·read·error'⏎···· with 'Error·reading·directory·/mock/dir:',·'Directory·read·error'

(prettier/prettier)


[error] 184-184: Delete ····

(prettier/prettier)


[error] 187-187: Delete ··

(prettier/prettier)


[error] 190-190: Expected an error object to be thrown.

(no-throw-literal)


[error] 192-192: Delete ····

(prettier/prettier)


[error] 194-194: Delete ····

(prettier/prettier)


[error] 201-201: Delete ····

(prettier/prettier)


[error] 204-204: Delete ····

(prettier/prettier)


[error] 207-207: Expected an error object to be thrown.

(no-throw-literal)


[error] 209-209: Delete ····

(prettier/prettier)


[error] 217-217: Delete ··

(prettier/prettier)


[error] 220-220: Delete ····

(prettier/prettier)


[error] 222-222: Delete ····

(prettier/prettier)


[error] 226-226: Delete ····

(prettier/prettier)


[error] 229-229: Delete ····

(prettier/prettier)


[error] 231-231: Delete ····

(prettier/prettier)


[error] 233-233: Delete ····

(prettier/prettier)


[error] 236-236: Insert

(prettier/prettier)

.github/workflows/if-nodejs-pr-testing.yml (1)

119-163: Workflow logic looks good
The newly introduced steps correctly run and capture the test:locales output, then provide helpful feedback on PRs.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 129-129: trailing spaces

(trailing-spaces)


[error] 146-146: trailing spaces

(trailing-spaces)


[error] 148-148: trailing spaces

(trailing-spaces)


[error] 152-152: trailing spaces

(trailing-spaces)

scripts/utils/check-locales.ts (1)

1-176: Locale validation logic is robust
The code thoroughly checks for missing keys, logs descriptive errors, and exits with the correct status code. Great job!

🧰 Tools
🪛 ESLint

[error] 1-6: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 2-2: 'path' imported multiple times.

(import/no-duplicates)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


[error] 6-6: path import should occur before import of ../utils/logger

(import/order)


[error] 6-6: 'path' imported multiple times.

(import/no-duplicates)


[error] 13-19: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 18-18: Unexpected trailing comma.

(comma-dangle)


[error] 28-41: Expected blank line after variable declarations.

(newline-after-var)


[error] 28-31: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 29-29: 'obj' is already declared in the upper scope on line 27 column 22.

(@typescript-eslint/no-shadow)


[error] 30-30: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Expected blank line after variable declarations.

(newline-after-var)


[error] 35-37: Expected blank line before this statement.

(padding-line-between-statements)


[error] 38-38: Expected blank line before this statement.

(padding-line-between-statements)


[error] 39-39: Delete ,

(prettier/prettier)


[error] 39-39: Unexpected trailing comma.

(comma-dangle)


[error] 42-42: Expected blank line before this statement.

(padding-line-between-statements)


[error] 53-53: Expected blank line after variable declarations.

(newline-after-var)


[error] 54-69: Expected blank line before this statement.

(padding-line-between-statements)


[error] 58-58: Expected blank line after variable declarations.

(newline-after-var)


[error] 59-67: Expected blank line before this statement.

(padding-line-between-statements)


[error] 60-60: Expected blank line after variable declarations.

(newline-after-var)


[error] 61-61: Expected blank line before this statement.

(padding-line-between-statements)


[error] 66-66: Expected blank line before this statement.

(padding-line-between-statements)


[error] 68-68: Delete ,

(prettier/prettier)


[error] 68-68: Unexpected trailing comma.

(comma-dangle)


[error] 74-74: Expected blank line before this statement.

(padding-line-between-statements)


[error] 92-92: Expected blank line before this statement.

(padding-line-between-statements)


[error] 101-101: Expected blank line after variable declarations.

(newline-after-var)


[error] 102-102: Expected blank line before this statement.

(padding-line-between-statements)


[error] 104-107: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 106-106: Unexpected trailing comma.

(comma-dangle)


[error] 107-107: Unexpected trailing comma.

(comma-dangle)


[error] 111-113: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 112-112: Unexpected trailing comma.

(comma-dangle)


[error] 118-120: Expected blank line after variable declarations.

(newline-after-var)


[error] 118-120: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 119-119: Unexpected trailing comma.

(comma-dangle)


[error] 121-121: Expected blank line before this statement.

(padding-line-between-statements)


[error] 121-121: Unexpected use of continue statement.

(no-continue)


[error] 123-125: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 124-124: Unexpected trailing comma.

(comma-dangle)


[error] 130-132: Expected blank line after variable declarations.

(newline-after-var)


[error] 130-132: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 131-131: Unexpected trailing comma.

(comma-dangle)


[error] 133-133: Expected blank line before this statement.

(padding-line-between-statements)


[error] 134-134: Delete ,

(prettier/prettier)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


[error] 137-139: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 138-138: '_' is defined but never used.

(no-unused-vars)


[error] 138-138: Unexpected trailing comma.

(comma-dangle)


[error] 143-148: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 144-146: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 145-145: Unexpected trailing comma.

(comma-dangle)


[error] 152-161: Unexpected negated condition.

(no-negated-condition)


[error] 153-155: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 154-154: Unexpected trailing comma.

(comma-dangle)


[error] 157-159: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 158-158: Unexpected trailing comma.

(comma-dangle)


[error] 170-170: Run autofix to sort these exports!

(simple-import-sort/exports)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
tests/check-locales.test.js (5)

1-40: Consolidate the duplicate path module mock

There's an unnecessary duplication of the path module mock on lines 11-18 and 31-39. While the second mock adds dirname, it would be better to consolidate these into a single mock to improve readability and maintainability.

- jest.mock('path', () => {
-   const originalPath = jest.requireActual('path');
-   return {
-     ...originalPath,
-     resolve: jest.fn().mockReturnValue('/mock/path/locales'),
-     join: jest.fn().mockImplementation((...args) => args.join('/'))
-   };
- });
- 
- // ... other code
-
- jest.mock('path', () => {
-   const originalPath = jest.requireActual('path');
-   return {
-     ...originalPath,
-     dirname: jest.fn().mockReturnValue('/mock/dir'),
-     resolve: jest.fn().mockReturnValue('/mock/path/locales'),
-     join: jest.fn().mockImplementation((...args) => args.join('/'))
-   };
- });
+ jest.mock('path', () => {
+   const originalPath = jest.requireActual('path');
+   return {
+     ...originalPath,
+     dirname: jest.fn().mockReturnValue('/mock/dir'),
+     resolve: jest.fn().mockReturnValue('/mock/path/locales'),
+     join: jest.fn().mockImplementation((...args) => args.join('/'))
+   };
+ });
🧰 Tools
🪛 ESLint

[error] 1-1: 'path' is assigned a value but never used.

(no-unused-vars)


[error] 28-28: Missing file extension "ts" for "../scripts/utils/check-locales"

(import/extensions)


[error] 29-29: Missing file extension "ts" for "../scripts/utils/logger"

(import/extensions)


28-29: Add .ts file extensions to imports

Add the file extensions to improve module resolution and conform to ESLint rules.

- const { validateLocales, extractKeys, readJSONFilesInDir } = require('../scripts/utils/check-locales');
- const { logger } = require('../scripts/utils/logger');
+ const { validateLocales, extractKeys, readJSONFilesInDir } = require('../scripts/utils/check-locales.ts');
+ const { logger } = require('../scripts/utils/logger.ts');
🧰 Tools
🪛 ESLint

[error] 28-28: Missing file extension "ts" for "../scripts/utils/check-locales"

(import/extensions)


[error] 29-29: Missing file extension "ts" for "../scripts/utils/logger"

(import/extensions)


73-92: Remove unused variable

The variable localesDir is defined but never used in the test. Consider removing it for cleaner code.

- const localesDir = '/mock/path/locales';
🧰 Tools
🪛 ESLint

[error] 74-74: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 76-76: Delete ····

(prettier/prettier)


[error] 78-78: Delete ····

(prettier/prettier)


[error] 82-82: Delete ····

(prettier/prettier)


[error] 85-85: Delete ····

(prettier/prettier)


[error] 87-87: Delete ····

(prettier/prettier)


[error] 89-89: Delete ····

(prettier/prettier)


94-123: Remove unnecessary else after return

On lines 112-114, remove the unnecessary else block after the return statement to simplify the code.

  if (filePath.includes('en')) {
    return '{"key1":"value1","key2":"value2"}';
- } else {
-   return '{"key1":"value1"}';
  }
+ return '{"key1":"value1"}';

Also, remove the unused localesDir variable similar to the previous test.

🧰 Tools
🪛 ESLint

[error] 95-95: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 97-97: Delete ····

(prettier/prettier)


[error] 99-99: Delete ····

(prettier/prettier)


[error] 101-101: Delete ····

(prettier/prettier)


[error] 105-105: Delete ····

(prettier/prettier)


[error] 108-108: Delete ····

(prettier/prettier)


[error] 112-114: Unnecessary 'else' after 'return'.

(no-else-return)


[error] 116-116: Delete ····

(prettier/prettier)


[error] 118-118: Delete ····

(prettier/prettier)


[error] 119-121: Replace ⏎······"❌·Language·'de'·is·missing·these·keys:·key2"⏎···· with "❌·Language·'de'·is·missing·these·keys:·key2"

(prettier/prettier)


152-169: Simplify error creation

The error creation at line 159 is unnecessarily complex. The new Error(testError) is creating an Error with an Error object as its message. Simplify it as follows:

- throw new Error(testError);
+ throw testError;
🧰 Tools
🪛 ESLint

[error] 156-156: Delete ····

(prettier/prettier)


[error] 161-161: Delete ····

(prettier/prettier)


[error] 163-163: Delete ····

(prettier/prettier)


[error] 164-167: Replace ⏎······expect.stringContaining(Error·reading),⏎······expect.any(String)⏎···· with expect.stringContaining(Error·reading),·expect.any(String)

(prettier/prettier)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0dbd7f6 and 5975d62.

📒 Files selected for processing (1)
  • tests/check-locales.test.js (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
tests/check-locales.test.js (1)
scripts/utils/check-locales.ts (3)
  • extractKeys (170-170)
  • readJSONFilesInDir (170-170)
  • validateLocales (170-170)
🪛 ESLint
tests/check-locales.test.js

[error] 1-1: 'path' is assigned a value but never used.

(no-unused-vars)


[error] 28-28: Missing file extension "ts" for "../scripts/utils/check-locales"

(import/extensions)


[error] 29-29: Missing file extension "ts" for "../scripts/utils/logger"

(import/extensions)


[error] 43-43: Delete ··

(prettier/prettier)


[error] 57-57: Delete ····

(prettier/prettier)


[error] 64-64: Delete ····

(prettier/prettier)


[error] 66-66: Delete ····

(prettier/prettier)


[error] 69-69: Delete ,

(prettier/prettier)


[error] 74-74: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 76-76: Delete ····

(prettier/prettier)


[error] 78-78: Delete ····

(prettier/prettier)


[error] 82-82: Delete ····

(prettier/prettier)


[error] 85-85: Delete ····

(prettier/prettier)


[error] 87-87: Delete ····

(prettier/prettier)


[error] 89-89: Delete ····

(prettier/prettier)


[error] 95-95: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 97-97: Delete ····

(prettier/prettier)


[error] 99-99: Delete ····

(prettier/prettier)


[error] 101-101: Delete ····

(prettier/prettier)


[error] 105-105: Delete ····

(prettier/prettier)


[error] 108-108: Delete ····

(prettier/prettier)


[error] 112-114: Unnecessary 'else' after 'return'.

(no-else-return)


[error] 116-116: Delete ····

(prettier/prettier)


[error] 118-118: Delete ····

(prettier/prettier)


[error] 119-121: Replace ⏎······"❌·Language·'de'·is·missing·these·keys:·key2"⏎···· with "❌·Language·'de'·is·missing·these·keys:·key2"

(prettier/prettier)


[error] 127-127: Delete ····

(prettier/prettier)


[error] 131-131: Delete ····

(prettier/prettier)


[error] 133-133: Delete ····

(prettier/prettier)


[error] 134-137: Replace ⏎······'Error·validating·locales:',⏎······testError⏎···· with 'Error·validating·locales:',·testError

(prettier/prettier)


[error] 143-143: Delete ····

(prettier/prettier)


[error] 145-145: Delete ····

(prettier/prettier)


[error] 146-149: Replace ⏎······'No·language·directories·found·in',⏎······'/mock/path/locales'⏎···· with 'No·language·directories·found·in',·'/mock/path/locales'

(prettier/prettier)


[error] 156-156: Delete ····

(prettier/prettier)


[error] 161-161: Delete ····

(prettier/prettier)


[error] 163-163: Delete ····

(prettier/prettier)


[error] 164-167: Replace ⏎······expect.stringContaining(Error·reading),⏎······expect.any(String)⏎···· with expect.stringContaining(Error·reading),·expect.any(String)

(prettier/prettier)


[error] 173-173: Delete ····

(prettier/prettier)


[error] 177-177: Delete ····

(prettier/prettier)


[error] 179-179: Delete ····

(prettier/prettier)


[error] 180-183: Replace ⏎······'Error·reading·directory·/mock/dir:',⏎······'Directory·read·error'⏎···· with 'Error·reading·directory·/mock/dir:',·'Directory·read·error'

(prettier/prettier)


[error] 184-184: Delete ····

(prettier/prettier)


[error] 187-187: Delete ··

(prettier/prettier)


[error] 192-192: Delete ····

(prettier/prettier)


[error] 194-194: Delete ····

(prettier/prettier)


[error] 201-201: Delete ····

(prettier/prettier)


[error] 204-204: Delete ····

(prettier/prettier)


[error] 209-209: Delete ····

(prettier/prettier)


[error] 217-217: Delete ··

(prettier/prettier)


[error] 220-220: Delete ····

(prettier/prettier)


[error] 222-222: Delete ····

(prettier/prettier)


[error] 226-226: Delete ····

(prettier/prettier)


[error] 229-229: Delete ····

(prettier/prettier)


[error] 231-231: Delete ····

(prettier/prettier)


[error] 233-233: Delete ····

(prettier/prettier)


[error] 236-236: Insert

(prettier/prettier)

🔇 Additional comments (9)
tests/check-locales.test.js (9)

48-52: Looks good!

The test for the extractKeys function is well-implemented and verifies that keys are properly extracted from a nested JSON object.


54-71: Looks good!

The test for readJSONFilesInDir function properly verifies JSON file reading, skipping non-JSON files, and returning the expected output.

🧰 Tools
🪛 ESLint

[error] 57-57: Delete ····

(prettier/prettier)


[error] 64-64: Delete ····

(prettier/prettier)


[error] 66-66: Delete ····

(prettier/prettier)


[error] 69-69: Delete ,

(prettier/prettier)


125-139: Looks good!

This test properly verifies error handling during validation, ensuring that errors are caught, logged, and the process exits with the correct status code.

🧰 Tools
🪛 ESLint

[error] 127-127: Delete ····

(prettier/prettier)


[error] 131-131: Delete ····

(prettier/prettier)


[error] 133-133: Delete ····

(prettier/prettier)


[error] 134-137: Replace ⏎······'Error·validating·locales:',⏎······testError⏎···· with 'Error·validating·locales:',·testError

(prettier/prettier)


141-150: Looks good!

This test effectively verifies the behavior when no language directories are found, ensuring proper error logging.

🧰 Tools
🪛 ESLint

[error] 143-143: Delete ····

(prettier/prettier)


[error] 145-145: Delete ····

(prettier/prettier)


[error] 146-149: Replace ⏎······'No·language·directories·found·in',⏎······'/mock/path/locales'⏎···· with 'No·language·directories·found·in',·'/mock/path/locales'

(prettier/prettier)


170-186: Looks good!

This test properly verifies the behavior when directory reading fails, ensuring that the error is properly logged and an empty object is returned.

🧰 Tools
🪛 ESLint

[error] 173-173: Delete ····

(prettier/prettier)


[error] 177-177: Delete ····

(prettier/prettier)


[error] 179-179: Delete ····

(prettier/prettier)


[error] 180-183: Replace ⏎······'Error·reading·directory·/mock/dir:',⏎······'Directory·read·error'⏎···· with 'Error·reading·directory·/mock/dir:',·'Directory·read·error'

(prettier/prettier)


[error] 184-184: Delete ····

(prettier/prettier)


188-196: Throw a string instead of an Error object

The test name suggests it's testing handling of non-Error exceptions, but line 190 throws an Error object. This doesn't match the test description.

- throw new Error('String error');
+ throw 'String error';

However, this change would reintroduce the ESLint error that was fixed in previous commits. Consider renaming the test to "should handle Error exceptions during validation" instead.

🧰 Tools
🪛 ESLint

[error] 192-192: Delete ····

(prettier/prettier)


[error] 194-194: Delete ····

(prettier/prettier)


198-216: Looks good!

This test properly verifies the behavior when JSON parsing fails, ensuring that an empty object is returned for the file.

🧰 Tools
🪛 ESLint

[error] 201-201: Delete ····

(prettier/prettier)


[error] 204-204: Delete ····

(prettier/prettier)


[error] 209-209: Delete ····

(prettier/prettier)


218-235: Looks good!

This test effectively verifies the behavior when a language has no corresponding file, ensuring that validation still succeeds in this case.

🧰 Tools
🪛 ESLint

[error] 220-220: Delete ····

(prettier/prettier)


[error] 222-222: Delete ····

(prettier/prettier)


[error] 226-226: Delete ····

(prettier/prettier)


[error] 229-229: Delete ····

(prettier/prettier)


[error] 231-231: Delete ····

(prettier/prettier)


[error] 233-233: Delete ····

(prettier/prettier)


1-236: Overall assessment of test coverage

The test suite provides comprehensive coverage for the locale validation functionality, testing:

  1. Key extraction from JSON objects
  2. JSON file reading from directories
  3. Successful locale validation
  4. Detection of missing keys across languages
  5. Various error handling scenarios

This thorough test coverage ensures the robustness of the locale validation functionality, which aligns well with the PR objective of ensuring consistency of locale keys across different language folders.

🧰 Tools
🪛 ESLint

[error] 1-1: 'path' is assigned a value but never used.

(no-unused-vars)


[error] 28-28: Missing file extension "ts" for "../scripts/utils/check-locales"

(import/extensions)


[error] 29-29: Missing file extension "ts" for "../scripts/utils/logger"

(import/extensions)


[error] 43-43: Delete ··

(prettier/prettier)


[error] 57-57: Delete ····

(prettier/prettier)


[error] 64-64: Delete ····

(prettier/prettier)


[error] 66-66: Delete ····

(prettier/prettier)


[error] 69-69: Delete ,

(prettier/prettier)


[error] 74-74: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 76-76: Delete ····

(prettier/prettier)


[error] 78-78: Delete ····

(prettier/prettier)


[error] 82-82: Delete ····

(prettier/prettier)


[error] 85-85: Delete ····

(prettier/prettier)


[error] 87-87: Delete ····

(prettier/prettier)


[error] 89-89: Delete ····

(prettier/prettier)


[error] 95-95: 'localesDir' is assigned a value but never used.

(no-unused-vars)


[error] 97-97: Delete ····

(prettier/prettier)


[error] 99-99: Delete ····

(prettier/prettier)


[error] 101-101: Delete ····

(prettier/prettier)


[error] 105-105: Delete ····

(prettier/prettier)


[error] 108-108: Delete ····

(prettier/prettier)


[error] 112-114: Unnecessary 'else' after 'return'.

(no-else-return)


[error] 116-116: Delete ····

(prettier/prettier)


[error] 118-118: Delete ····

(prettier/prettier)


[error] 119-121: Replace ⏎······"❌·Language·'de'·is·missing·these·keys:·key2"⏎···· with "❌·Language·'de'·is·missing·these·keys:·key2"

(prettier/prettier)


[error] 127-127: Delete ····

(prettier/prettier)


[error] 131-131: Delete ····

(prettier/prettier)


[error] 133-133: Delete ····

(prettier/prettier)


[error] 134-137: Replace ⏎······'Error·validating·locales:',⏎······testError⏎···· with 'Error·validating·locales:',·testError

(prettier/prettier)


[error] 143-143: Delete ····

(prettier/prettier)


[error] 145-145: Delete ····

(prettier/prettier)


[error] 146-149: Replace ⏎······'No·language·directories·found·in',⏎······'/mock/path/locales'⏎···· with 'No·language·directories·found·in',·'/mock/path/locales'

(prettier/prettier)


[error] 156-156: Delete ····

(prettier/prettier)


[error] 161-161: Delete ····

(prettier/prettier)


[error] 163-163: Delete ····

(prettier/prettier)


[error] 164-167: Replace ⏎······expect.stringContaining(Error·reading),⏎······expect.any(String)⏎···· with expect.stringContaining(Error·reading),·expect.any(String)

(prettier/prettier)


[error] 173-173: Delete ····

(prettier/prettier)


[error] 177-177: Delete ····

(prettier/prettier)


[error] 179-179: Delete ····

(prettier/prettier)


[error] 180-183: Replace ⏎······'Error·reading·directory·/mock/dir:',⏎······'Directory·read·error'⏎···· with 'Error·reading·directory·/mock/dir:',·'Directory·read·error'

(prettier/prettier)


[error] 184-184: Delete ····

(prettier/prettier)


[error] 187-187: Delete ··

(prettier/prettier)


[error] 192-192: Delete ····

(prettier/prettier)


[error] 194-194: Delete ····

(prettier/prettier)


[error] 201-201: Delete ····

(prettier/prettier)


[error] 204-204: Delete ····

(prettier/prettier)


[error] 209-209: Delete ····

(prettier/prettier)


[error] 217-217: Delete ··

(prettier/prettier)


[error] 220-220: Delete ····

(prettier/prettier)


[error] 222-222: Delete ····

(prettier/prettier)


[error] 226-226: Delete ····

(prettier/prettier)


[error] 229-229: Delete ····

(prettier/prettier)


[error] 231-231: Delete ····

(prettier/prettier)


[error] 233-233: Delete ····

(prettier/prettier)


[error] 236-236: Insert

(prettier/prettier)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (6)
tests/check-locales.test.js (1)

31-31: Minor style issue

There's a trailing whitespace in the mockExit implementation.

-  const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { });
+  const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
🧰 Tools
🪛 ESLint

[error] 31-31: Delete ·

(prettier/prettier)

scripts/utils/check-locales.ts (5)

27-43: Well-implemented recursive key extraction

The extractKeys function effectively extracts all keys from a nested JSON object, including proper dot notation for nested structures. However, there's a variable shadowing issue with the obj parameter.

function extractKeys(obj: Record<string, any>): string[] {
  const extractNestedKeys = (
-    obj: Record<string, any>,
+    nestedObj: Record<string, any>,
    prefix: string = ''
  ): string[] => {
    return flatten(
-      Object.entries(obj).map(([key, value]) => {
+      Object.entries(nestedObj).map(([key, value]) => {
        const currentKey = prefix ? `${prefix}.${key}` : key;
        if (value !== null && typeof value === 'object') {
          return extractNestedKeys(value, currentKey);
        }
        return currentKey;
      })
    );
  };
  return extractNestedKeys(obj);
}
🧰 Tools
🪛 ESLint

[error] 28-41: Expected blank line after variable declarations.

(newline-after-var)


[error] 28-31: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 29-29: 'obj' is already declared in the upper scope on line 27 column 22.

(@typescript-eslint/no-shadow)


[error] 30-30: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Expected blank line after variable declarations.

(newline-after-var)


[error] 35-37: Expected blank line before this statement.

(padding-line-between-statements)


[error] 38-38: Expected blank line before this statement.

(padding-line-between-statements)


[error] 39-39: Delete ,

(prettier/prettier)


[error] 39-39: Unexpected trailing comma.

(comma-dangle)


[error] 42-42: Expected blank line before this statement.

(padding-line-between-statements)


80-162: Comprehensive locale validation implementation

The validateLocales function correctly:

  1. Identifies language directories
  2. Reads JSON files for each language
  3. Extracts keys for all files
  4. Identifies missing keys across languages
  5. Reports errors with detailed information
  6. Exits with appropriate status code

One potential improvement is to optimize the handling of negated conditions:

-    if (!hasErrors) {
-      logger.info(
-        '✅ All locale files have the same keys across all languages!'
-      );
-    } else {
-      logger.error(
-        '\n❌ Some translation keys are missing. Please fix the issues above.'
-      );
-      process.exit(1);
-    }
+    if (hasErrors) {
+      logger.error(
+        '\n❌ Some translation keys are missing. Please fix the issues above.'
+      );
+      process.exit(1);
+    } else {
+      logger.info(
+        '✅ All locale files have the same keys across all languages!'
+      );
+    }
🧰 Tools
🪛 ESLint

[error] 88-88: Expected blank line before this statement.

(padding-line-between-statements)


[error] 97-97: Expected blank line after variable declarations.

(newline-after-var)


[error] 98-98: Expected blank line before this statement.

(padding-line-between-statements)


[error] 100-103: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 102-102: Unexpected trailing comma.

(comma-dangle)


[error] 103-103: Unexpected trailing comma.

(comma-dangle)


[error] 107-109: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 108-108: Unexpected trailing comma.

(comma-dangle)


[error] 114-116: Expected blank line after variable declarations.

(newline-after-var)


[error] 114-116: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 115-115: Unexpected trailing comma.

(comma-dangle)


[error] 117-117: Expected blank line before this statement.

(padding-line-between-statements)


[error] 117-117: Unexpected use of continue statement.

(no-continue)


[error] 119-121: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 120-120: Unexpected trailing comma.

(comma-dangle)


[error] 126-128: Expected blank line after variable declarations.

(newline-after-var)


[error] 126-128: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 127-127: Unexpected trailing comma.

(comma-dangle)


[error] 129-129: Expected blank line before this statement.

(padding-line-between-statements)


[error] 130-130: Delete ,

(prettier/prettier)


[error] 130-130: Unexpected trailing comma.

(comma-dangle)


[error] 133-135: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 134-134: '_' is defined but never used.

(no-unused-vars)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


[error] 139-144: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 140-142: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 141-141: Unexpected trailing comma.

(comma-dangle)


[error] 148-157: Unexpected negated condition.

(no-negated-condition)


[error] 149-151: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 150-150: Unexpected trailing comma.

(comma-dangle)


[error] 153-155: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 154-154: Unexpected trailing comma.

(comma-dangle)


117-117: Consider refactoring the continue statement

Using continue statements can make code harder to follow. Consider restructuring this logic for better readability.

-      if (langsWithFile.length <= 1) continue;
+      if (langsWithFile.length <= 1) {
+        continue;
+      }

Or better yet:

-      if (langsWithFile.length <= 1) continue;
+      if (langsWithFile.length > 1) {
+        // Rest of the code inside this block
+      }
🧰 Tools
🪛 ESLint

[error] 117-117: Expected blank line before this statement.

(padding-line-between-statements)


[error] 117-117: Unexpected use of continue statement.

(no-continue)


133-145: Fix unused variable and loop function reference

There's an unused variable _ and a potential issue with referencing hasErrors in a loop function.

-      const langsWithMissingKeys = Object.entries(missingKeysByLang).filter(
-        ([_, missing]) => missing.length > 0
-      );
+      const langsWithMissingKeys = Object.entries(missingKeysByLang).filter(
+        ([, missing]) => missing.length > 0
+      );

      if (langsWithMissingKeys.length > 0) {
        logger.info(`\nMissing keys in '${file}':`);
+        let errorsFound = false;
        langsWithMissingKeys.forEach(([lang, missing]) => {
          logger.error(
            `❌ Language '${lang}' is missing these keys: ${missing.join(', ')}`
          );
-          hasErrors = true;
+          errorsFound = true;
        });
+        hasErrors = errorsFound;
      }
🧰 Tools
🪛 ESLint

[error] 133-135: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 134-134: '_' is defined but never used.

(no-unused-vars)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


[error] 139-144: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 140-142: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 141-141: Unexpected trailing comma.

(comma-dangle)


1-170: Consider adding more error details in catch blocks

The error handling could be improved by logging more details about the caught errors.

  } catch (error) {
-    logger.error('Error validating locales:');
+    logger.error('Error validating locales:', error instanceof Error ? error.message : String(error));
    process.exit(1);
  }

Similarly in the readJSONFilesInDir function:

  } catch (error) {
-    logger.error(`Error reading directory ${dir}`);
+    logger.error(`Error reading directory ${dir}:`, error instanceof Error ? error.message : String(error));
    return {};
  }
🧰 Tools
🪛 ESLint

[error] 1-6: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 2-2: 'path' imported multiple times.

(import/no-duplicates)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


[error] 6-6: path import should occur before import of ../utils/logger

(import/order)


[error] 6-6: 'path' imported multiple times.

(import/no-duplicates)


[error] 13-19: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 18-18: Unexpected trailing comma.

(comma-dangle)


[error] 28-41: Expected blank line after variable declarations.

(newline-after-var)


[error] 28-31: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 29-29: 'obj' is already declared in the upper scope on line 27 column 22.

(@typescript-eslint/no-shadow)


[error] 30-30: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Expected blank line after variable declarations.

(newline-after-var)


[error] 35-37: Expected blank line before this statement.

(padding-line-between-statements)


[error] 38-38: Expected blank line before this statement.

(padding-line-between-statements)


[error] 39-39: Delete ,

(prettier/prettier)


[error] 39-39: Unexpected trailing comma.

(comma-dangle)


[error] 42-42: Expected blank line before this statement.

(padding-line-between-statements)


[error] 53-53: Expected blank line after variable declarations.

(newline-after-var)


[error] 54-67: Expected blank line before this statement.

(padding-line-between-statements)


[error] 58-58: Expected blank line after variable declarations.

(newline-after-var)


[error] 59-65: Expected blank line before this statement.

(padding-line-between-statements)


[error] 60-60: Expected blank line after variable declarations.

(newline-after-var)


[error] 61-61: Expected blank line before this statement.

(padding-line-between-statements)


[error] 64-64: Expected blank line before this statement.

(padding-line-between-statements)


[error] 66-66: Delete ,

(prettier/prettier)


[error] 66-66: Unexpected trailing comma.

(comma-dangle)


[error] 70-70: Expected blank line before this statement.

(padding-line-between-statements)


[error] 88-88: Expected blank line before this statement.

(padding-line-between-statements)


[error] 97-97: Expected blank line after variable declarations.

(newline-after-var)


[error] 98-98: Expected blank line before this statement.

(padding-line-between-statements)


[error] 100-103: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 102-102: Unexpected trailing comma.

(comma-dangle)


[error] 103-103: Unexpected trailing comma.

(comma-dangle)


[error] 107-109: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 108-108: Unexpected trailing comma.

(comma-dangle)


[error] 114-116: Expected blank line after variable declarations.

(newline-after-var)


[error] 114-116: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 115-115: Unexpected trailing comma.

(comma-dangle)


[error] 117-117: Expected blank line before this statement.

(padding-line-between-statements)


[error] 117-117: Unexpected use of continue statement.

(no-continue)


[error] 119-121: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 120-120: Unexpected trailing comma.

(comma-dangle)


[error] 126-128: Expected blank line after variable declarations.

(newline-after-var)


[error] 126-128: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 127-127: Unexpected trailing comma.

(comma-dangle)


[error] 129-129: Expected blank line before this statement.

(padding-line-between-statements)


[error] 130-130: Delete ,

(prettier/prettier)


[error] 130-130: Unexpected trailing comma.

(comma-dangle)


[error] 133-135: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 134-134: '_' is defined but never used.

(no-unused-vars)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


[error] 139-144: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 140-142: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 141-141: Unexpected trailing comma.

(comma-dangle)


[error] 148-157: Unexpected negated condition.

(no-negated-condition)


[error] 149-151: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 150-150: Unexpected trailing comma.

(comma-dangle)


[error] 153-155: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 154-154: Unexpected trailing comma.

(comma-dangle)


[error] 164-164: Run autofix to sort these exports!

(simple-import-sort/exports)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5975d62 and 805fe0d.

📒 Files selected for processing (2)
  • scripts/utils/check-locales.ts (1 hunks)
  • tests/check-locales.test.js (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
tests/check-locales.test.js (1)
scripts/utils/check-locales.ts (3)
  • extractKeys (164-164)
  • readJSONFilesInDir (164-164)
  • validateLocales (164-164)
🪛 ESLint
scripts/utils/check-locales.ts

[error] 1-6: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 2-2: 'path' imported multiple times.

(import/no-duplicates)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


[error] 6-6: path import should occur before import of ../utils/logger

(import/order)


[error] 6-6: 'path' imported multiple times.

(import/no-duplicates)


[error] 13-19: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 18-18: Unexpected trailing comma.

(comma-dangle)


[error] 28-41: Expected blank line after variable declarations.

(newline-after-var)


[error] 28-31: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 29-29: 'obj' is already declared in the upper scope on line 27 column 22.

(@typescript-eslint/no-shadow)


[error] 30-30: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Expected blank line after variable declarations.

(newline-after-var)


[error] 35-37: Expected blank line before this statement.

(padding-line-between-statements)


[error] 38-38: Expected blank line before this statement.

(padding-line-between-statements)


[error] 39-39: Delete ,

(prettier/prettier)


[error] 39-39: Unexpected trailing comma.

(comma-dangle)


[error] 42-42: Expected blank line before this statement.

(padding-line-between-statements)


[error] 53-53: Expected blank line after variable declarations.

(newline-after-var)


[error] 54-67: Expected blank line before this statement.

(padding-line-between-statements)


[error] 58-58: Expected blank line after variable declarations.

(newline-after-var)


[error] 59-65: Expected blank line before this statement.

(padding-line-between-statements)


[error] 60-60: Expected blank line after variable declarations.

(newline-after-var)


[error] 61-61: Expected blank line before this statement.

(padding-line-between-statements)


[error] 64-64: Expected blank line before this statement.

(padding-line-between-statements)


[error] 66-66: Delete ,

(prettier/prettier)


[error] 66-66: Unexpected trailing comma.

(comma-dangle)


[error] 70-70: Expected blank line before this statement.

(padding-line-between-statements)


[error] 88-88: Expected blank line before this statement.

(padding-line-between-statements)


[error] 97-97: Expected blank line after variable declarations.

(newline-after-var)


[error] 98-98: Expected blank line before this statement.

(padding-line-between-statements)


[error] 100-103: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 102-102: Unexpected trailing comma.

(comma-dangle)


[error] 103-103: Unexpected trailing comma.

(comma-dangle)


[error] 107-109: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 108-108: Unexpected trailing comma.

(comma-dangle)


[error] 114-116: Expected blank line after variable declarations.

(newline-after-var)


[error] 114-116: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 115-115: Unexpected trailing comma.

(comma-dangle)


[error] 117-117: Expected blank line before this statement.

(padding-line-between-statements)


[error] 117-117: Unexpected use of continue statement.

(no-continue)


[error] 119-121: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 120-120: Unexpected trailing comma.

(comma-dangle)


[error] 126-128: Expected blank line after variable declarations.

(newline-after-var)


[error] 126-128: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 127-127: Unexpected trailing comma.

(comma-dangle)


[error] 129-129: Expected blank line before this statement.

(padding-line-between-statements)


[error] 130-130: Delete ,

(prettier/prettier)


[error] 130-130: Unexpected trailing comma.

(comma-dangle)


[error] 133-135: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 134-134: '_' is defined but never used.

(no-unused-vars)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


[error] 139-144: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 140-142: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 141-141: Unexpected trailing comma.

(comma-dangle)


[error] 148-157: Unexpected negated condition.

(no-negated-condition)


[error] 149-151: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 150-150: Unexpected trailing comma.

(comma-dangle)


[error] 153-155: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 154-154: Unexpected trailing comma.

(comma-dangle)


[error] 164-164: Run autofix to sort these exports!

(simple-import-sort/exports)

tests/check-locales.test.js

[error] 31-31: Delete ·

(prettier/prettier)


[error] 58-58: Delete ,

(prettier/prettier)


[error] 105-107: Replace ⏎······"❌·Language·'de'·is·missing·these·keys:·key2"⏎···· with "❌·Language·'de'·is·missing·these·keys:·key2"

(prettier/prettier)


[error] 112-112: Delete ·

(prettier/prettier)


[error] 116-118: Replace ⏎······'Error·validating·locales:'⏎···· with 'Error·validating·locales:'

(prettier/prettier)


[error] 127-130: Replace ⏎······'No·language·directories·found·in',⏎······'/mock/path/locales'⏎···· with 'No·language·directories·found·in',·'/mock/path/locales'

(prettier/prettier)


[error] 138-138: Delete ·

(prettier/prettier)


[error] 142-144: Replace ⏎······expect.stringContaining(Error·reading)⏎···· with expect.stringContaining(Error·reading)

(prettier/prettier)


[error] 150-150: Delete ·

(prettier/prettier)


[error] 154-156: Replace ⏎······'Error·reading·directory·/mock/dir'⏎···· with 'Error·reading·directory·/mock/dir'

(prettier/prettier)


[error] 209-209: Insert

(prettier/prettier)

🔇 Additional comments (14)
tests/check-locales.test.js (12)

1-18: Test setup looks good!

The test file properly mocks the necessary dependencies (fs, logger, path) for isolated testing. The structure is clean and follows Jest best practices.


30-41: Good test coverage for the extractKeys function

This test validates that the function correctly handles nested JSON objects and produces dot notation keys as expected.

🧰 Tools
🪛 ESLint

[error] 31-31: Delete ·

(prettier/prettier)


43-60: Test for readJSONFilesInDir is thorough

The test correctly validates that:

  1. Only JSON files are read from the directory
  2. Each file's content is properly parsed
  3. Non-JSON files are ignored

This ensures the function behaves as expected in real-world scenarios.

🧰 Tools
🪛 ESLint

[error] 58-58: Delete ,

(prettier/prettier)


62-80: Well-structured happy path test for validateLocales

The test effectively verifies that when all locale files have the same keys across languages, the validation passes without errors and logs the expected success message.


82-109: Good negative test case for missing keys

This test effectively verifies that when keys are missing in locale files, the validation fails with appropriate error messages and exits with code 1.

🧰 Tools
🪛 ESLint

[error] 105-107: Replace ⏎······"❌·Language·'de'·is·missing·these·keys:·key2"⏎···· with "❌·Language·'de'·is·missing·these·keys:·key2"

(prettier/prettier)


111-120: Error handling test is comprehensive

The test verifies that the validation function properly handles errors during execution and exits with the correct status code.

🧰 Tools
🪛 ESLint

[error] 112-112: Delete ·

(prettier/prettier)


[error] 116-118: Replace ⏎······'Error·validating·locales:'⏎···· with 'Error·validating·locales:'

(prettier/prettier)


122-131: Edge case handling for empty language directories

Good coverage of the scenario where no language directories are found, ensuring proper error logging.

🧰 Tools
🪛 ESLint

[error] 127-130: Replace ⏎······'No·language·directories·found·in',⏎······'/mock/path/locales'⏎···· with 'No·language·directories·found·in',·'/mock/path/locales'

(prettier/prettier)


133-146: Test for JSON file reading errors

This test ensures that when JSON file reading fails, the function logs an error and returns an empty object for that file rather than crashing, which is a good defensive programming practice.

🧰 Tools
🪛 ESLint

[error] 138-138: Delete ·

(prettier/prettier)


[error] 142-144: Replace ⏎······expect.stringContaining(Error·reading)⏎···· with expect.stringContaining(Error·reading)

(prettier/prettier)


147-159: Directory reading error handling test

The test validates proper error handling when directory reading fails, making the code more robust against file system issues.

🧰 Tools
🪛 ESLint

[error] 150-150: Delete ·

(prettier/prettier)


[error] 154-156: Replace ⏎······'Error·reading·directory·/mock/dir'⏎···· with 'Error·reading·directory·/mock/dir'

(prettier/prettier)


161-169: Exception handling test for non-Error objects

The test correctly verifies behavior when errors are thrown, ensuring process.exit is called.


171-189: JSON parsing error handling test

The test confirms that JSON parsing errors are properly handled by mocking the global JSON.parse function, and the original function is restored afterward, which is a good testing practice.


191-208: Edge case test for languages with no files

This test verifies that the validation function handles the case when a language has no files properly.

scripts/utils/check-locales.ts (2)

51-72: Robust implementation of JSON file reading

The readJSONFilesInDir function is well-implemented with proper error handling for both file reading and directory access errors. It correctly filters for JSON files and returns an empty object when errors occur.

🧰 Tools
🪛 ESLint

[error] 53-53: Expected blank line after variable declarations.

(newline-after-var)


[error] 54-67: Expected blank line before this statement.

(padding-line-between-statements)


[error] 58-58: Expected blank line after variable declarations.

(newline-after-var)


[error] 59-65: Expected blank line before this statement.

(padding-line-between-statements)


[error] 60-60: Expected blank line after variable declarations.

(newline-after-var)


[error] 61-61: Expected blank line before this statement.

(padding-line-between-statements)


[error] 64-64: Expected blank line before this statement.

(padding-line-between-statements)


[error] 66-66: Delete ,

(prettier/prettier)


[error] 66-66: Unexpected trailing comma.

(comma-dangle)


[error] 70-70: Expected blank line before this statement.

(padding-line-between-statements)


164-169: Clean module export and entry point

The export statement and entry point logic are well-implemented. The istanbul ignore next directive appropriately excludes the entry point from test coverage.

🧰 Tools
🪛 ESLint

[error] 164-164: Run autofix to sort these exports!

(simple-import-sort/exports)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
scripts/utils/check-locales.ts (1)

1-5: 🛠️ Refactor suggestion

Fix import statements.

There are several issues with the imports:

  1. Import ordering doesn't follow the project's convention
  2. Useless path segments in the logger import
-import fs from 'fs';
-import path, { dirname } from 'path';
-import { fileURLToPath } from 'url';
-import lodash from 'lodash';
-import { logger } from '../utils/logger';
+import fs from 'fs';
+import lodash from 'lodash';
+import path, { dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { logger } from './logger';
🧰 Tools
🪛 ESLint

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)

🧹 Nitpick comments (8)
.github/workflows/if-nodejs-pr-testing.yml (1)

129-140: Fix trailing whitespaces in the YAML file.

There are several lines with trailing whitespaces which should be removed to maintain code quality.

-          
+
-            
+
-            
+
-            
+
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 129-129: trailing spaces

(trailing-spaces)


[error] 134-134: trailing spaces

(trailing-spaces)


[error] 137-137: trailing spaces

(trailing-spaces)


[error] 140-140: trailing spaces

(trailing-spaces)

scripts/utils/check-locales.ts (7)

12-18: Fix formatting issues.

There are trailing commas and unnecessary line breaks in the path.resolve() call.

 const localesDir = path.resolve(
-  currentDirPath,
-  '..',
-  '..',
-  'public',
-  'locales',
+  currentDirPath, '..', '..', 'public', 'locales'
 );
🧰 Tools
🪛 ESLint

[error] 12-18: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 17-17: Unexpected trailing comma.

(comma-dangle)


50-71: Fix formatting and improve error handling.

There are several formatting issues including trailing commas and missing blank lines. Also, the error handling should log the actual error for better debugging.

 function readJSONFilesInDir(dir: string): Record<string, any> {
   try {
     const files = fs.readdirSync(dir);
+
     return fromPairs(
       files
         .filter((file) => path.extname(file) === '.json')
         .map((file) => {
           const filePath = path.join(dir, file);
+
           try {
             const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
+
             return [file, content];
           } catch (error) {
-            logger.error(`Error reading ${filePath}`);
+            logger.error(`Error reading ${filePath}:`, error);
+
             return [file, {}];
           }
-        }),
+        })
     );
   } catch (error) {
-    logger.error(`Error reading directory ${dir}`);
+    logger.error(`Error reading directory ${dir}:`, error);
+
     return {};
   }
 }
🧰 Tools
🪛 ESLint

[error] 52-52: Expected blank line after variable declarations.

(newline-after-var)


[error] 53-66: Expected blank line before this statement.

(padding-line-between-statements)


[error] 57-57: Expected blank line after variable declarations.

(newline-after-var)


[error] 58-64: Expected blank line before this statement.

(padding-line-between-statements)


[error] 59-59: Expected blank line after variable declarations.

(newline-after-var)


[error] 60-60: Expected blank line before this statement.

(padding-line-between-statements)


[error] 63-63: Expected blank line before this statement.

(padding-line-between-statements)


[error] 65-65: Delete ,

(prettier/prettier)


[error] 65-65: Unexpected trailing comma.

(comma-dangle)


[error] 69-69: Expected blank line before this statement.

(padding-line-between-statements)


116-116: Reconsider use of continue statement.

The continue statement on line 116 skips to the next iteration when a file is only present in one language. Consider refactoring for clarity.

-      if (langsWithFile.length <= 1) continue;
+      if (langsWithFile.length <= 1) {
+        continue;
+      }
🧰 Tools
🪛 ESLint

[error] 116-116: Expected blank line before this statement.

(padding-line-between-statements)


[error] 116-116: Unexpected use of continue statement.

(no-continue)


133-134: Replace unused variable with underscore.

The variable _ is defined but never used. Consider using a more descriptive name or replacing it with the standard underscore variable.

 const langsWithMissingKeys = Object.entries(missingKeysByLang).filter(
-        ([_, missing]) => missing.length > 0,
+        ([_lang, missing]) => missing.length > 0
       );
🧰 Tools
🪛 ESLint

[error] 132-134: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 133-133: '_' is defined but never used.

(no-unused-vars)


[error] 133-133: Unexpected trailing comma.

(comma-dangle)


147-156: Simplify conditional logic.

The negated condition can be refactored to a more readable positive condition.

-    if (!hasErrors) {
-      logger.info(
-        '✅ All locale files have the same keys across all languages!',
-      );
-    } else {
+    if (hasErrors) {
       logger.error(
-        '\n❌ Some translation keys are missing. Please fix the issues above.',
+        '\n❌ Some translation keys are missing. Please fix the issues above.'
       );
       process.exit(1);
+    } else {
+      logger.info(
+        '✅ All locale files have the same keys across all languages!'
+      );
     }
🧰 Tools
🪛 ESLint

[error] 147-156: Unexpected negated condition.

(no-negated-condition)


[error] 148-150: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 149-149: Unexpected trailing comma.

(comma-dangle)


[error] 152-154: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 153-153: Unexpected trailing comma.

(comma-dangle)


157-160: Improve error logging.

The current error logging doesn't include the actual error object, making debugging difficult.

  } catch (error) {
-    logger.error('Error validating locales:');
+    logger.error('Error validating locales:', error);
     process.exit(1);
  }

163-163: Sort exported items alphabetically.

For consistency and better readability, consider sorting the exported items alphabetically.

-export { validateLocales, extractKeys, readJSONFilesInDir };
+export { extractKeys, readJSONFilesInDir, validateLocales };
🧰 Tools
🪛 ESLint

[error] 163-163: Run autofix to sort these exports!

(simple-import-sort/exports)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 805fe0d and b540c1d.

📒 Files selected for processing (2)
  • .github/workflows/if-nodejs-pr-testing.yml (1 hunks)
  • scripts/utils/check-locales.ts (1 hunks)
🧰 Additional context used
🪛 ESLint
scripts/utils/check-locales.ts

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


[error] 12-18: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 17-17: Unexpected trailing comma.

(comma-dangle)


[error] 27-40: Expected blank line after variable declarations.

(newline-after-var)


[error] 27-30: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 28-28: 'obj' is already declared in the upper scope on line 26 column 22.

(@typescript-eslint/no-shadow)


[error] 29-29: Unexpected trailing comma.

(comma-dangle)


[error] 33-33: Expected blank line after variable declarations.

(newline-after-var)


[error] 34-36: Expected blank line before this statement.

(padding-line-between-statements)


[error] 37-37: Expected blank line before this statement.

(padding-line-between-statements)


[error] 38-38: Delete ,

(prettier/prettier)


[error] 38-38: Unexpected trailing comma.

(comma-dangle)


[error] 41-41: Expected blank line before this statement.

(padding-line-between-statements)


[error] 52-52: Expected blank line after variable declarations.

(newline-after-var)


[error] 53-66: Expected blank line before this statement.

(padding-line-between-statements)


[error] 57-57: Expected blank line after variable declarations.

(newline-after-var)


[error] 58-64: Expected blank line before this statement.

(padding-line-between-statements)


[error] 59-59: Expected blank line after variable declarations.

(newline-after-var)


[error] 60-60: Expected blank line before this statement.

(padding-line-between-statements)


[error] 63-63: Expected blank line before this statement.

(padding-line-between-statements)


[error] 65-65: Delete ,

(prettier/prettier)


[error] 65-65: Unexpected trailing comma.

(comma-dangle)


[error] 69-69: Expected blank line before this statement.

(padding-line-between-statements)


[error] 87-87: Expected blank line before this statement.

(padding-line-between-statements)


[error] 96-96: Expected blank line after variable declarations.

(newline-after-var)


[error] 97-97: Expected blank line before this statement.

(padding-line-between-statements)


[error] 99-102: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 101-101: Unexpected trailing comma.

(comma-dangle)


[error] 102-102: Unexpected trailing comma.

(comma-dangle)


[error] 106-108: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 107-107: Unexpected trailing comma.

(comma-dangle)


[error] 113-115: Expected blank line after variable declarations.

(newline-after-var)


[error] 113-115: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 114-114: Unexpected trailing comma.

(comma-dangle)


[error] 116-116: Expected blank line before this statement.

(padding-line-between-statements)


[error] 116-116: Unexpected use of continue statement.

(no-continue)


[error] 118-120: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 119-119: Unexpected trailing comma.

(comma-dangle)


[error] 125-127: Expected blank line after variable declarations.

(newline-after-var)


[error] 125-127: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 126-126: Unexpected trailing comma.

(comma-dangle)


[error] 128-128: Expected blank line before this statement.

(padding-line-between-statements)


[error] 129-129: Delete ,

(prettier/prettier)


[error] 129-129: Unexpected trailing comma.

(comma-dangle)


[error] 132-134: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 133-133: '_' is defined but never used.

(no-unused-vars)


[error] 133-133: Unexpected trailing comma.

(comma-dangle)


[error] 138-143: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 139-141: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 140-140: Unexpected trailing comma.

(comma-dangle)


[error] 147-156: Unexpected negated condition.

(no-negated-condition)


[error] 148-150: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 149-149: Unexpected trailing comma.

(comma-dangle)


[error] 152-154: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 153-153: Unexpected trailing comma.

(comma-dangle)


[error] 163-163: Run autofix to sort these exports!

(simple-import-sort/exports)

🪛 YAMLlint (1.35.1)
.github/workflows/if-nodejs-pr-testing.yml

[error] 129-129: trailing spaces

(trailing-spaces)


[error] 134-134: trailing spaces

(trailing-spaces)


[error] 137-137: trailing spaces

(trailing-spaces)


[error] 140-140: trailing spaces

(trailing-spaces)


[error] 155-155: trailing spaces

(trailing-spaces)


[error] 157-157: trailing spaces

(trailing-spaces)


[error] 161-161: trailing spaces

(trailing-spaces)

🔇 Additional comments (4)
.github/workflows/if-nodejs-pr-testing.yml (3)

119-144: Implementation looks good!

The locale check implementation correctly captures and formats the output from the locale test script. The error handling with set +e and set -e ensures that the workflow continues even if the locale check fails, which is appropriate for this kind of validation.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 129-129: trailing spaces

(trailing-spaces)


[error] 134-134: trailing spaces

(trailing-spaces)


[error] 137-137: trailing spaces

(trailing-spaces)


[error] 140-140: trailing spaces

(trailing-spaces)


147-163: LGTM - PR comment mechanism is properly implemented.

The PR comment mechanism properly uses the sticky-pull-request-comment action with appropriate headers and conditions, consistent with the existing markdown checking implementation.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 155-155: trailing spaces

(trailing-spaces)


[error] 157-157: trailing spaces

(trailing-spaces)


[error] 161-161: trailing spaces

(trailing-spaces)


164-171: LGTM - Cleanup mechanism implemented correctly.

The cleanup step correctly removes the comment when no issues are found, which keeps the PR clean and ensures users only see relevant feedback.

scripts/utils/check-locales.ts (1)

165-168: LGTM - Direct script execution logic.

The direct script execution logic is properly implemented with istanbul ignore for test coverage.

@anshgoyalevil anshgoyalevil self-assigned this Mar 29, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
scripts/utils/check-locales.ts (3)

56-56: 🛠️ Refactor suggestion

Include error details in log message

The error logging should include the actual error object to provide more context for debugging.

-            logger.error(`Error reading ${filePath}`);
+            logger.error(`Error reading ${filePath}`, error);

149-149: 🛠️ Refactor suggestion

Consider throwing an error instead of exiting the process

Using process.exit(1) forcefully terminates the process, which may not be ideal when this function is used as a module in other contexts.

-      process.exit(1);
+      throw new Error('Locale validation failed: some translation keys are missing');

This would allow calling code to handle the error appropriately, especially if this module is imported and used in different contexts.


62-62: 🛠️ Refactor suggestion

Include error details in directory error log

Similar to the file error logging, the directory error logging should include the actual error object.

-    logger.error(`Error reading directory ${dir}`);
+    logger.error(`Error reading directory ${dir}`, error);
🧹 Nitpick comments (4)
scripts/utils/check-locales.ts (4)

1-5: Optimize import statements

The import statements could be improved for better organization and clarity:

  1. The import path for logger is using a relative path that could be simplified
  2. The imports should be sorted for consistency
 import fs from 'fs';
-import path, { dirname } from 'path';
+import lodash from 'lodash';
+import path, { dirname } from 'path';
 import { fileURLToPath } from 'url';
-import lodash from 'lodash';
-import { logger } from '../utils/logger';
+import { logger } from './logger';
🧰 Tools
🪛 ESLint

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


12-12: Simplify path resolution

The path resolution can be simplified to a single line for better readability.

-const localesDir = path.resolve(currentDirPath, '..', '..', 'public', 'locales');
+const localesDir = path.resolve(currentDirPath, '..', '..', 'public', 'locales');

20-36: Fix variable shadowing in extractKeys function

The nested function has a parameter obj that shadows the outer function's parameter, which could cause confusion for maintainers.

 function extractKeys(obj: Record<string, any>): string[] {
   const extractNestedKeys = (
-    obj: Record<string, any>,
+    nestedObj: Record<string, any>,
     prefix: string = '',
   ): string[] => {
+
     return flatten(
-      Object.entries(obj).map(([key, value]) => {
+      Object.entries(nestedObj).map(([key, value]) => {
         const currentKey = prefix ? `${prefix}.${key}` : key;
+
         if (value !== null && typeof value === 'object') {
           return extractNestedKeys(value, currentKey);
         }
+
         return currentKey;
-      }),
+      })
     );
   };
+
   return extractNestedKeys(obj);
 }

I understand from the retrieved learnings that you prefer not to worry about variable shadowing, so feel free to ignore this suggestion if you believe it doesn't impact code readability in your context.

🧰 Tools
🪛 ESLint

[error] 21-34: Expected blank line after variable declarations.

(newline-after-var)


[error] 21-24: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 22-22: 'obj' is already declared in the upper scope on line 20 column 22.

(@typescript-eslint/no-shadow)


[error] 23-23: Unexpected trailing comma.

(comma-dangle)


[error] 27-27: Expected blank line after variable declarations.

(newline-after-var)


[error] 28-30: Expected blank line before this statement.

(padding-line-between-statements)


[error] 31-31: Expected blank line before this statement.

(padding-line-between-statements)


[error] 32-32: Delete ,

(prettier/prettier)


[error] 32-32: Unexpected trailing comma.

(comma-dangle)


[error] 35-35: Expected blank line before this statement.

(padding-line-between-statements)


110-110: Consider replacing 'continue' with early return pattern

The use of continue is flagged by ESLint. Consider using an early return pattern or conditional block for better code clarity.

-      if (langsWithFile.length <= 1) continue;
+      if (langsWithFile.length <= 1) {
+        continue;
+      }

Alternatively:

-      if (langsWithFile.length <= 1) continue;
+      // Skip files that exist in only one or zero languages
+      if (langsWithFile.length > 1) {
+        // Rest of the code
+      }
🧰 Tools
🪛 ESLint

[error] 110-110: Expected blank line before this statement.

(padding-line-between-statements)


[error] 110-110: Unexpected use of continue statement.

(no-continue)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6651422 and 45f5d83.

📒 Files selected for processing (1)
  • scripts/utils/check-locales.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
scripts/utils/check-locales.ts (2)
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:138-143
Timestamp: 2025-03-23T12:42:49.503Z
Learning: For the locale validation function in check-locales.ts, the team has decided that modifying the outer scope variable 'hasErrors' inside the forEach loop is acceptable despite the ESLint warning about unsafe references in loop functions.
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:26-42
Timestamp: 2025-03-23T12:43:10.614Z
Learning: The user anshgoyalevil prefers not to worry about variable shadowing in JavaScript/TypeScript code as they believe scoped variables don't make much difference.
🪛 ESLint
scripts/utils/check-locales.ts

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 5-5: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


[error] 21-34: Expected blank line after variable declarations.

(newline-after-var)


[error] 21-24: Replace ⏎····obj:·Record<string,·any>,⏎····prefix:·string·=·'',⏎·· with obj:·Record<string,·any>,·prefix:·string·=·''

(prettier/prettier)


[error] 22-22: 'obj' is already declared in the upper scope on line 20 column 22.

(@typescript-eslint/no-shadow)


[error] 23-23: Unexpected trailing comma.

(comma-dangle)


[error] 27-27: Expected blank line after variable declarations.

(newline-after-var)


[error] 28-30: Expected blank line before this statement.

(padding-line-between-statements)


[error] 31-31: Expected blank line before this statement.

(padding-line-between-statements)


[error] 32-32: Delete ,

(prettier/prettier)


[error] 32-32: Unexpected trailing comma.

(comma-dangle)


[error] 35-35: Expected blank line before this statement.

(padding-line-between-statements)


[error] 46-46: Expected blank line after variable declarations.

(newline-after-var)


[error] 47-60: Expected blank line before this statement.

(padding-line-between-statements)


[error] 51-51: Expected blank line after variable declarations.

(newline-after-var)


[error] 52-58: Expected blank line before this statement.

(padding-line-between-statements)


[error] 53-53: Expected blank line after variable declarations.

(newline-after-var)


[error] 54-54: Expected blank line before this statement.

(padding-line-between-statements)


[error] 57-57: Expected blank line before this statement.

(padding-line-between-statements)


[error] 59-59: Delete ,

(prettier/prettier)


[error] 59-59: Unexpected trailing comma.

(comma-dangle)


[error] 63-63: Expected blank line before this statement.

(padding-line-between-statements)


[error] 81-81: Expected blank line before this statement.

(padding-line-between-statements)


[error] 90-90: Expected blank line after variable declarations.

(newline-after-var)


[error] 91-91: Expected blank line before this statement.

(padding-line-between-statements)


[error] 93-96: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 95-95: Unexpected trailing comma.

(comma-dangle)


[error] 96-96: Unexpected trailing comma.

(comma-dangle)


[error] 100-102: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 101-101: Unexpected trailing comma.

(comma-dangle)


[error] 107-109: Expected blank line after variable declarations.

(newline-after-var)


[error] 107-109: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 108-108: Unexpected trailing comma.

(comma-dangle)


[error] 110-110: Expected blank line before this statement.

(padding-line-between-statements)


[error] 110-110: Unexpected use of continue statement.

(no-continue)


[error] 112-114: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 113-113: Unexpected trailing comma.

(comma-dangle)


[error] 119-121: Expected blank line after variable declarations.

(newline-after-var)


[error] 119-121: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 120-120: Unexpected trailing comma.

(comma-dangle)


[error] 122-122: Expected blank line before this statement.

(padding-line-between-statements)


[error] 123-123: Delete ,

(prettier/prettier)


[error] 123-123: Unexpected trailing comma.

(comma-dangle)


[error] 126-128: Replace ⏎········([_,·missing])·=>·missing.length·>·0,⏎······ with ([_,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 127-127: '_' is defined but never used.

(no-unused-vars)


[error] 127-127: Unexpected trailing comma.

(comma-dangle)


[error] 132-137: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 133-135: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


[error] 141-150: Unexpected negated condition.

(no-negated-condition)


[error] 142-144: Replace ⏎········'✅·All·locale·files·have·the·same·keys·across·all·languages!',⏎······ with '✅·All·locale·files·have·the·same·keys·across·all·languages!'

(prettier/prettier)


[error] 143-143: Unexpected trailing comma.

(comma-dangle)


[error] 146-148: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 147-147: Unexpected trailing comma.

(comma-dangle)


[error] 157-157: Run autofix to sort these exports!

(simple-import-sort/exports)

🔇 Additional comments (3)
scripts/utils/check-locales.ts (3)

132-138: Function in loop references outer variable

The function declared inside the forEach loop references the outer variable hasErrors, which ESLint flags as an unsafe reference in loop functions.

Based on retrieved learnings, I understand this pattern has been accepted by the team for this specific case, so I'll approve this implementation.

🧰 Tools
🪛 ESLint

[error] 132-137: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 133-135: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 134-134: Unexpected trailing comma.

(comma-dangle)


157-157: LGTM: Exports are well-organized

The exported functions provide a good API for this utility. The script can be used both as a module and as a standalone script.

🧰 Tools
🪛 ESLint

[error] 157-157: Run autofix to sort these exports!

(simple-import-sort/exports)


159-162: LGTM: Script execution logic is well-implemented

The execution logic with istanbul ignore is properly implemented. This allows the script to be run directly while also making it testable.

@asyncapi-bot asyncapi-bot added the bounty AsyncAPI Bounty program related label label Apr 21, 2025
@aeworxet
Copy link
Contributor

@asyncapi/bounty_team

@aeworxet aeworxet moved this to In Progress in Bounty Program Apr 21, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
scripts/utils/check-locales.ts (1)

6-6: ⚠️ Potential issue

Fix import path for logger to avoid useless path segments.

Use a relative path (./logger) since check-locales.ts and logger.ts are in the same directory. This resolves the import/no-useless-path-segments error and ensures the import points to the correct file.

Apply this diff:

-import { logger } from '../utils/logger';
+import { logger } from './logger';
🧰 Tools
🪛 ESLint

[error] 6-6: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)

🧹 Nitpick comments (2)
scripts/utils/check-locales.ts (2)

174-178: Remove redundant try/catch wrapper around script execution.

The catch block only rethrows the error, which is unnecessary and triggers a no-useless-catch warning. Simplify by calling validateLocales() directly.

Proposed diff:

-  try {
-    validateLocales();
-  } catch (error) {
-    throw error;
-  }
+  validateLocales();
🧰 Tools
🪛 ESLint

[error] 174-178: Unnecessary try/catch wrapper.

(no-useless-catch)


146-151: Consider avoiding forEach side effects on outer-scope variables.

Modifying hasErrors inside a forEach can trigger lint warnings and be less clear. As an optional improvement, you could switch to a for…of loop or use a local flag within the loop and then assign to hasErrors after, enhancing readability and maintainability.

🧰 Tools
🪛 ESLint

[error] 146-151: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 147-149: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 148-148: Unexpected trailing comma.

(comma-dangle)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd470ce and 9d37bd5.

📒 Files selected for processing (1)
  • scripts/utils/check-locales.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
scripts/utils/check-locales.ts (3)
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:138-143
Timestamp: 2025-03-23T12:42:49.503Z
Learning: For the locale validation function in check-locales.ts, the team has decided that modifying the outer scope variable 'hasErrors' inside the forEach loop is acceptable despite the ESLint warning about unsafe references in loop functions.
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:26-42
Timestamp: 2025-03-23T12:43:10.614Z
Learning: The user anshgoyalevil prefers not to worry about variable shadowing in JavaScript/TypeScript code as they believe scoped variables don't make much difference.
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:122-129
Timestamp: 2025-04-20T16:05:16.482Z
Learning: In the AsyncAPI website project, Next.js throws errors at runtime when locale files are missing, making additional validation for missing files unnecessary in the check-locales script.
🪛 Biome (1.9.4)
scripts/utils/check-locales.ts

[error] 177-179: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

🪛 ESLint
scripts/utils/check-locales.ts

[error] 6-6: Useless path segments for "../utils/logger", should be "./logger"

(import/no-useless-path-segments)


[error] 22-25: Replace ⏎····nestedObj:·Record<string,·any>,⏎····prefix·=·'',⏎·· with nestedObj:·Record<string,·any>,·prefix·=·''

(prettier/prettier)


[error] 24-24: Unexpected trailing comma.

(comma-dangle)


[error] 35-35: Delete ,

(prettier/prettier)


[error] 35-35: Unexpected trailing comma.

(comma-dangle)


[error] 67-67: Delete ,

(prettier/prettier)


[error] 67-67: Unexpected trailing comma.

(comma-dangle)


[error] 89-89: Expected blank line after variable declarations.

(newline-after-var)


[error] 90-90: Expected blank line before this statement.

(padding-line-between-statements)


[error] 100-100: Expected blank line after variable declarations.

(newline-after-var)


[error] 101-101: Expected blank line before this statement.

(padding-line-between-statements)


[error] 103-106: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 105-105: Unexpected trailing comma.

(comma-dangle)


[error] 106-106: Unexpected trailing comma.

(comma-dangle)


[error] 110-112: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 111-111: Unexpected trailing comma.

(comma-dangle)


[error] 117-119: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 118-118: Unexpected trailing comma.

(comma-dangle)


[error] 122-122: Unexpected use of continue statement.

(no-continue)


[error] 125-127: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 126-126: Unexpected trailing comma.

(comma-dangle)


[error] 132-134: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 133-133: Unexpected trailing comma.

(comma-dangle)


[error] 137-137: Delete ,

(prettier/prettier)


[error] 137-137: Unexpected trailing comma.

(comma-dangle)


[error] 140-142: Replace ⏎········([,·missing])·=>·missing.length·>·0,⏎······ with ([,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 141-141: Unexpected trailing comma.

(comma-dangle)


[error] 146-151: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 147-149: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 148-148: Unexpected trailing comma.

(comma-dangle)


[error] 156-158: Expected blank line after variable declarations.

(newline-after-var)


[error] 156-158: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 157-157: Unexpected trailing comma.

(comma-dangle)


[error] 159-159: Expected blank line before this statement.

(padding-line-between-statements)


[error] 170-170: Run autofix to sort these exports!

(simple-import-sort/exports)


[error] 174-178: Unnecessary try/catch wrapper.

(no-useless-catch)

⏰ Context from checks skipped due to timeout of 180000ms (3)
  • GitHub Check: Redirect rules - asyncapi-website
  • GitHub Check: Header rules - asyncapi-website
  • GitHub Check: Pages changed - asyncapi-website

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (7)
scripts/utils/check-locales.ts (7)

22-37: Improve the recursive function implementation to avoid unnecessary array operations

The extractNestedKeys function currently builds and flattens arrays for every object level, which can be inefficient for deeply nested objects. Consider using an accumulator pattern to avoid these intermediate array operations.

function extractKeys(obj: Record<string, any>): string[] {
  const extractNestedKeys = (
    nestedObj: Record<string, any>,
    prefix = '',
+   result: string[] = []
  ): string[] => {
-    return flatten(
-      Object.entries(nestedObj).map(([key, value]) => {
-        const currentKey = prefix ? `${prefix}.${key}` : key;
-
-        if (value !== null && typeof value === 'object') {
-          return extractNestedKeys(value, currentKey);
-        }
-
-        return currentKey;
-      }),
-    );
+    for (const [key, value] of Object.entries(nestedObj)) {
+      const currentKey = prefix ? `${prefix}.${key}` : key;
+      
+      if (value !== null && typeof value === 'object') {
+        extractNestedKeys(value, currentKey, result);
+      } else {
+        result.push(currentKey);
+      }
+    }
+    
+    return result;
  };

  return extractNestedKeys(obj);
}
🧰 Tools
🪛 ESLint

[error] 22-25: Replace ⏎····nestedObj:·Record<string,·any>,⏎····prefix·=·'',⏎·· with nestedObj:·Record<string,·any>,·prefix·=·''

(prettier/prettier)


[error] 24-24: Unexpected trailing comma.

(comma-dangle)


[error] 35-35: Delete ,

(prettier/prettier)


[error] 35-35: Unexpected trailing comma.

(comma-dangle)


63-66: Consider enhancing error handling for invalid JSON files

When a JSON file can't be parsed, you're returning an empty object. This might hide issues because keys will appear to be missing rather than indicating a problem with the file itself.

try {
  const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));

  return [file, content];
} catch (error) {
  logger.error(`Error reading ${filePath}`, error);

-  return [file, {}];
+  // Mark files with parsing errors distinctly instead of treating them as empty
+  return [file, { __ERROR__: `Failed to parse: ${error.message}` }];
}

83-92: Add directory existence check before reading languages

The code assumes that the locales directory exists, but it might not in certain environments or during initial setup. Adding an explicit check would make the error message more helpful.

function validateLocales(): void {
  try {
+    // Check if the locales directory exists before proceeding
+    if (!fs.existsSync(localesDir)) {
+      const error = new Error(`Locales directory not found at ${localesDir}`);
+      logger.error('Locales directory not found:', error);
+      throw error;
+    }
+    
    const languages = fs
      .readdirSync(localesDir)
      .filter((file) => fs.statSync(path.join(localesDir, file)).isDirectory());

    if (languages.length === 0) {
      const error = new Error(`No language directories found in ${localesDir}`);
      logger.error('No language directories found:', error);
      throw error;
    }
🧰 Tools
🪛 ESLint

[error] 89-89: Expected blank line after variable declarations.

(newline-after-var)


[error] 90-90: Expected blank line before this statement.

(padding-line-between-statements)


121-123: Consider logging information about skipped files

Currently, files that exist in only one language are silently skipped. This is consistent with your design, but logging this information might be helpful for debugging or informational purposes.

if (langsWithFile.length <= 1) {
+  logger.info(`Skipping '${file}' (only found in ${langsWithFile.length} language${langsWithFile.length === 1 ? '' : 's'})`);
  continue;
}
🧰 Tools
🪛 ESLint

[error] 122-122: Unexpected use of continue statement.

(no-continue)


94-108: Performance optimization for file operations

The current implementation reads and processes each language directory sequentially. For projects with many languages and files, this could be improved with parallel processing.

-    for (const lang of languages) {
-      const langDir = path.join(localesDir, lang);
-      languageFiles[lang] = readJSONFilesInDir(langDir);
-      fileKeys[lang] = fromPairs(
-        Object.entries(languageFiles[lang]).map(([file, content]) => [
-          file,
-          extractKeys(content),
-        ]),
-      );
-    }
+    // Process language directories in parallel for better performance
+    const processLanguage = (lang: string) => {
+      const langDir = path.join(localesDir, lang);
+      const files = readJSONFilesInDir(langDir);
+      return {
+        lang,
+        files,
+        keys: fromPairs(
+          Object.entries(files).map(([file, content]) => [
+            file,
+            extractKeys(content),
+          ]),
+        ),
+      };
+    };
+    
+    // Use Promise.all for parallel processing
+    const results = await Promise.all(languages.map(processLanguage));
+    
+    // Reconstruct the data structures
+    results.forEach(({ lang, files, keys }) => {
+      languageFiles[lang] = files;
+      fileKeys[lang] = keys;
+    });

Note: This would require making validateLocales an async function and updating the call in the self-executing section to handle the Promise.

🧰 Tools
🪛 ESLint

[error] 100-100: Expected blank line after variable declarations.

(newline-after-var)


[error] 101-101: Expected blank line before this statement.

(padding-line-between-statements)


[error] 103-106: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 105-105: Unexpected trailing comma.

(comma-dangle)


[error] 106-106: Unexpected trailing comma.

(comma-dangle)


155-161: Enhance error reporting with detailed statistics

When validation fails, providing more detailed statistics could help users quickly assess the scope of the issues.

if (hasErrors) {
+  // Calculate statistics for more informative error reporting
+  const totalMissingCount = Object.values(missingKeysByLang || {})
+    .flatMap(keys => keys)
+    .length;
+  const affectedLanguagesCount = Object.entries(missingKeysByLang || {})
+    .filter(([, keys]) => keys.length > 0)
+    .length;
+  const affectedFilesCount = /* calculate from previous loop */;
+
  const error = new Error(
-    '\n❌ Some translation keys are missing. Please fix the issues above.',
+    `\n❌ Validation failed: ${totalMissingCount} missing keys across ${affectedLanguagesCount} languages. Please fix the issues above.`,
  );
  logger.error('Translation validation failed:', error);
  throw error;
}
🧰 Tools
🪛 ESLint

[error] 156-158: Expected blank line after variable declarations.

(newline-after-var)


[error] 156-158: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 157-157: Unexpected trailing comma.

(comma-dangle)


[error] 159-159: Expected blank line before this statement.

(padding-line-between-statements)


114-153: Consider performance improvements for large datasets

For large locale datasets with many files and keys, the current implementation could be optimized to reduce memory usage and improve performance.

The outer loop processes each file sequentially, but the inner operations could be streamlined:

  1. Use Sets instead of Arrays where appropriate to make lookups faster
  2. Avoid multiple array transformations (filter, map, flatten) that create intermediary arrays
  3. Consider breaking down the processing into smaller functions for better maintainability

Example optimization for the missing keys detection:

// This function could replace the current nested map/filter operations
function findMissingKeys(allLanguageKeys: Record<string, Set<string>>): Record<string, string[]> {
  // Create a set of all unique keys across all languages
  const allKeysSet = new Set<string>();
  for (const langKeys of Object.values(allLanguageKeys)) {
    for (const key of langKeys) {
      allKeysSet.add(key);
    }
  }
  
  // Find missing keys for each language
  const missingKeys: Record<string, string[]> = {};
  for (const [lang, langKeys] of Object.entries(allLanguageKeys)) {
    const missing = Array.from(allKeysSet).filter(key => !langKeys.has(key));
    if (missing.length > 0) {
      missingKeys[lang] = missing;
    }
  }
  
  return missingKeys;
}
🧰 Tools
🪛 ESLint

[error] 117-119: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 118-118: Unexpected trailing comma.

(comma-dangle)


[error] 122-122: Unexpected use of continue statement.

(no-continue)


[error] 125-127: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 126-126: Unexpected trailing comma.

(comma-dangle)


[error] 132-134: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 133-133: Unexpected trailing comma.

(comma-dangle)


[error] 137-137: Delete ,

(prettier/prettier)


[error] 137-137: Unexpected trailing comma.

(comma-dangle)


[error] 140-142: Replace ⏎········([,·missing])·=>·missing.length·>·0,⏎······ with ([,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 141-141: Unexpected trailing comma.

(comma-dangle)


[error] 146-151: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 147-149: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 148-148: Unexpected trailing comma.

(comma-dangle)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d37bd5 and fbc5408.

📒 Files selected for processing (1)
  • scripts/utils/check-locales.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
scripts/utils/check-locales.ts (3)
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:138-143
Timestamp: 2025-03-23T12:42:49.503Z
Learning: For the locale validation function in check-locales.ts, the team has decided that modifying the outer scope variable 'hasErrors' inside the forEach loop is acceptable despite the ESLint warning about unsafe references in loop functions.
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:26-42
Timestamp: 2025-03-23T12:43:10.614Z
Learning: The user anshgoyalevil prefers not to worry about variable shadowing in JavaScript/TypeScript code as they believe scoped variables don't make much difference.
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:122-129
Timestamp: 2025-04-20T16:05:16.482Z
Learning: In the AsyncAPI website project, Next.js throws errors at runtime when locale files are missing, making additional validation for missing files unnecessary in the check-locales script.
🧬 Code Graph Analysis (1)
scripts/utils/check-locales.ts (1)
scripts/utils/logger.ts (1)
  • logger (18-18)
🪛 ESLint
scripts/utils/check-locales.ts

[error] 22-25: Replace ⏎····nestedObj:·Record<string,·any>,⏎····prefix·=·'',⏎·· with nestedObj:·Record<string,·any>,·prefix·=·''

(prettier/prettier)


[error] 24-24: Unexpected trailing comma.

(comma-dangle)


[error] 35-35: Delete ,

(prettier/prettier)


[error] 35-35: Unexpected trailing comma.

(comma-dangle)


[error] 67-67: Delete ,

(prettier/prettier)


[error] 67-67: Unexpected trailing comma.

(comma-dangle)


[error] 89-89: Expected blank line after variable declarations.

(newline-after-var)


[error] 90-90: Expected blank line before this statement.

(padding-line-between-statements)


[error] 100-100: Expected blank line after variable declarations.

(newline-after-var)


[error] 101-101: Expected blank line before this statement.

(padding-line-between-statements)


[error] 103-106: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 105-105: Unexpected trailing comma.

(comma-dangle)


[error] 106-106: Unexpected trailing comma.

(comma-dangle)


[error] 110-112: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 111-111: Unexpected trailing comma.

(comma-dangle)


[error] 117-119: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 118-118: Unexpected trailing comma.

(comma-dangle)


[error] 122-122: Unexpected use of continue statement.

(no-continue)


[error] 125-127: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 126-126: Unexpected trailing comma.

(comma-dangle)


[error] 132-134: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 133-133: Unexpected trailing comma.

(comma-dangle)


[error] 137-137: Delete ,

(prettier/prettier)


[error] 137-137: Unexpected trailing comma.

(comma-dangle)


[error] 140-142: Replace ⏎········([,·missing])·=>·missing.length·>·0,⏎······ with ([,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 141-141: Unexpected trailing comma.

(comma-dangle)


[error] 146-151: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 147-149: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 148-148: Unexpected trailing comma.

(comma-dangle)


[error] 156-158: Expected blank line after variable declarations.

(newline-after-var)


[error] 156-158: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 157-157: Unexpected trailing comma.

(comma-dangle)


[error] 159-159: Expected blank line before this statement.

(padding-line-between-statements)


[error] 170-170: Run autofix to sort these exports!

(simple-import-sort/exports)

⏰ Context from checks skipped due to timeout of 180000ms (3)
  • GitHub Check: Redirect rules - asyncapi-website
  • GitHub Check: Header rules - asyncapi-website
  • GitHub Check: Pages changed - asyncapi-website
🔇 Additional comments (3)
scripts/utils/check-locales.ts (3)

146-151: Unsafe reference to outer variable in loop function

As previously discussed, this is modifying the outer scope variable hasErrors inside a forEach loop, which triggers an ESLint warning. Based on your preferences (as noted in the learnings), you've indicated this is acceptable to you since it doesn't cause functional issues in this case.

🧰 Tools
🪛 ESLint

[error] 146-151: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 147-149: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 148-148: Unexpected trailing comma.

(comma-dangle)


172-175: LGTM: Self-executing code with proper guards

The use of the istanbul ignore next comment for the self-executing code section is a good practice. It ensures this section is excluded from code coverage reports while allowing the script to be run directly. The check for process.argv[1] is also the proper way to detect when the file is being executed directly.


1-6: Imports are well organized

The imports are properly structured with built-in Node.js modules first, followed by third-party packages, and finally local imports. This follows best practices for import ordering.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
tests/check-locales.test.js (3)

43-60: Well-structured test for readJSONFilesInDir.

This test properly verifies that JSON files are read and parsed correctly while non-JSON files are ignored.

However, there's a minor formatting issue flagged by ESLint:

-    expect(result).toEqual({
-      'file1.json': { key1: 'value1' },
-      'file2.json': { key2: 'value2' },
-    });
+    expect(result).toEqual({
+      'file1.json': { key1: 'value1' },
+      'file2.json': { key2: 'value2' }
+    });
🧰 Tools
🪛 ESLint

[error] 58-58: Delete ,

(prettier/prettier)


131-144: Good test for directory reading errors.

This test ensures proper error handling when reading a directory fails.

There's a minor formatting issue flagged by ESLint:

-    expect(logger.error).toHaveBeenCalledWith(
-      'Error reading directory /mock/dir',
-      expect.any(Error)
-    );
+    expect(logger.error).toHaveBeenCalledWith(
+      'Error reading directory /mock/dir', expect.any(Error)
+    );
🧰 Tools
🪛 ESLint

[error] 134-134: Delete ·

(prettier/prettier)


[error] 138-141: Replace ⏎······'Error·reading·directory·/mock/dir',⏎······expect.any(Error)⏎···· with 'Error·reading·directory·/mock/dir',·expect.any(Error)

(prettier/prettier)


146-152: Misleading test description.

The test description mentions "non-Error exceptions", but the mock is actually throwing an Error object.

-  it('should handle non-Error exceptions during validation', () => {
+  it('should handle Error exceptions during validation', () => {
scripts/utils/check-locales.ts (2)

8-19: Good utility function setup and path resolution.

The path resolution for the locales directory is well-structured.

However, there's a formatting issue flagged by ESLint:

-const localesDir = path.resolve(
-  currentDirPath,
-  '..',
-  '..',
-  'public',
-  'locales',
-);
+const localesDir = path.resolve(currentDirPath, '..', '..', 'public', 'locales');

This is a minor stylistic issue that doesn't affect functionality.

🧰 Tools
🪛 ESLint

[error] 13-19: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 18-18: Unexpected trailing comma.

(comma-dangle)


180-185: Clean exports and self-execution logic.

The file exports the necessary functions for testing and self-executes validateLocales when run directly.

ESLint suggests sorting the exports, which is a minor improvement:

-export { validateLocales, extractKeys, readJSONFilesInDir };
+export { extractKeys, readJSONFilesInDir, validateLocales };
🧰 Tools
🪛 ESLint

[error] 180-180: Run autofix to sort these exports!

(simple-import-sort/exports)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fbc5408 and cb267bb.

📒 Files selected for processing (2)
  • scripts/utils/check-locales.ts (1 hunks)
  • tests/check-locales.test.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
scripts/utils/check-locales.ts (3)
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:138-143
Timestamp: 2025-03-23T12:42:49.503Z
Learning: For the locale validation function in check-locales.ts, the team has decided that modifying the outer scope variable 'hasErrors' inside the forEach loop is acceptable despite the ESLint warning about unsafe references in loop functions.
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:26-42
Timestamp: 2025-03-23T12:43:10.614Z
Learning: The user anshgoyalevil prefers not to worry about variable shadowing in JavaScript/TypeScript code as they believe scoped variables don't make much difference.
Learnt from: anshgoyalevil
PR: asyncapi/website#3950
File: scripts/utils/check-locales.ts:122-129
Timestamp: 2025-04-20T16:05:16.482Z
Learning: In the AsyncAPI website project, Next.js throws errors at runtime when locale files are missing, making additional validation for missing files unnecessary in the check-locales script.
🧬 Code Graph Analysis (2)
scripts/utils/check-locales.ts (1)
scripts/utils/logger.ts (1)
  • logger (18-18)
tests/check-locales.test.js (3)
scripts/utils/check-locales.ts (3)
  • extractKeys (180-180)
  • readJSONFilesInDir (180-180)
  • validateLocales (180-180)
utils/i18n.ts (1)
  • languages (20-20)
scripts/utils/logger.ts (1)
  • logger (18-18)
🪛 ESLint
scripts/utils/check-locales.ts

[error] 13-19: Replace ⏎··currentDirPath,⏎··'..',⏎··'..',⏎··'public',⏎··'locales',⏎ with currentDirPath,·'..',·'..',·'public',·'locales'

(prettier/prettier)


[error] 18-18: Unexpected trailing comma.

(comma-dangle)


[error] 28-32: Replace ⏎····nestedObj:·Record<string,·any>,⏎····prefix·=·'',⏎····result:·string[]·=·[],⏎·· with nestedObj:·Record<string,·any>,·prefix·=·'',·result:·string[]·=·[]

(prettier/prettier)


[error] 31-31: Unexpected trailing comma.

(comma-dangle)


[error] 74-74: Delete ,

(prettier/prettier)


[error] 74-74: Unexpected trailing comma.

(comma-dangle)


[error] 96-96: Expected blank line after variable declarations.

(newline-after-var)


[error] 97-97: Expected blank line before this statement.

(padding-line-between-statements)


[error] 107-107: Expected blank line after variable declarations.

(newline-after-var)


[error] 108-108: Expected blank line before this statement.

(padding-line-between-statements)


[error] 110-113: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 112-112: Unexpected trailing comma.

(comma-dangle)


[error] 113-113: Unexpected trailing comma.

(comma-dangle)


[error] 117-119: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 118-118: Unexpected trailing comma.

(comma-dangle)


[error] 124-126: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 125-125: Unexpected trailing comma.

(comma-dangle)


[error] 129-131: Replace ⏎··········Skipping·'${file}'·(only·found·in·${langsWithFile.length}·language),⏎········ with Skipping·'${file}'·(only·found·in·${langsWithFile.length}·language)

(prettier/prettier)


[error] 130-130: Unexpected trailing comma.

(comma-dangle)


[error] 132-132: Unexpected use of continue statement.

(no-continue)


[error] 135-137: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 136-136: Unexpected trailing comma.

(comma-dangle)


[error] 142-144: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 143-143: Unexpected trailing comma.

(comma-dangle)


[error] 147-147: Delete ,

(prettier/prettier)


[error] 147-147: Unexpected trailing comma.

(comma-dangle)


[error] 150-152: Replace ⏎········([,·missing])·=>·missing.length·>·0,⏎······ with ([,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 151-151: Unexpected trailing comma.

(comma-dangle)


[error] 156-161: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 157-159: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 158-158: Unexpected trailing comma.

(comma-dangle)


[error] 166-168: Expected blank line after variable declarations.

(newline-after-var)


[error] 166-168: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 167-167: Unexpected trailing comma.

(comma-dangle)


[error] 169-169: Expected blank line before this statement.

(padding-line-between-statements)


[error] 180-180: Run autofix to sort these exports!

(simple-import-sort/exports)

tests/check-locales.test.js

[error] 31-31: Delete ·

(prettier/prettier)


[error] 58-58: Delete ,

(prettier/prettier)


[error] 120-120: Delete ·

(prettier/prettier)


[error] 134-134: Delete ·

(prettier/prettier)


[error] 138-141: Replace ⏎······'Error·reading·directory·/mock/dir',⏎······expect.any(Error)⏎···· with 'Error·reading·directory·/mock/dir',·expect.any(Error)

(prettier/prettier)

⏰ Context from checks skipped due to timeout of 180000ms (4)
  • GitHub Check: Redirect rules - asyncapi-website
  • GitHub Check: Header rules - asyncapi-website
  • GitHub Check: Pages changed - asyncapi-website
  • GitHub Check: Test NodeJS PR - macos-13
🔇 Additional comments (24)
tests/check-locales.test.js (15)

1-5: Good use of explicit mock object for filesystem operations.

The mock filesystem approach is well-structured, making it easy to control the behavior of these functions throughout your tests.


7-8: Clean imports for the tested functions.

Your imports correctly reference the functions being tested and the logger dependency.


10-18: Well-structured logger mock.

The logger mock properly implements all required methods (error, warn, info) that will be used in the tests.


20-28: Comprehensive path module mock.

This mock preserves original path functionality while overriding specific methods needed for testing. The use of mockImplementation for the join method is particularly good for maintaining path-like behavior.


30-35: Good setup for the test suite.

The mockExit spy prevents actual process termination during tests, and the beforeEach hook ensures clean mocks between test runs.

🧰 Tools
🪛 ESLint

[error] 31-31: Delete ·

(prettier/prettier)


37-41: Simple and effective test for extractKeys.

This test clearly verifies that the extractKeys function correctly extracts dot-notation keys from nested objects.


62-79: Good happy path test for validateLocales.

This test properly verifies that the function logs success when all locale files have consistent keys across languages.


81-99: Thorough test for missing keys scenario.

This test correctly verifies that validateLocales throws an error when keys are missing in some languages.


101-107: Good error handling test.

This test ensures that errors during validation are properly propagated.


109-113: Comprehensive edge case testing.

Good test for the scenario when no language directories are found.


115-129: Thorough testing of JSON file reading errors.

This test properly verifies error logging and the fallback to an empty object when file reading fails.

🧰 Tools
🪛 ESLint

[error] 120-120: Delete ·

(prettier/prettier)


154-172: Careful handling of global JSON.parse override.

Good approach using a try-finally block to ensure the global JSON.parse method is restored even if the test fails.


174-191: Good edge case testing for empty language directories.

This test thoroughly verifies that the validation correctly handles the scenario when a language has no locale files.


193-211: Appropriate test for success case with multiple languages.

This test confirms that validateLocales correctly identifies when all keys match across languages.


213-230: Comprehensive test for skipping files found in only one language.

This test properly verifies that files unique to a single language are skipped with appropriate logging.

scripts/utils/check-locales.ts (9)

1-6: Well-organized imports.

The imports are properly organized by type, with core Node.js modules first, followed by external dependencies and internal modules.


21-47: Well-documented and efficient key extraction function.

The extractKeys function is well-documented with JSDoc comments and effectively extracts nested keys from JSON objects using dot notation. The recursive implementation is clean and works correctly.

🧰 Tools
🪛 ESLint

[error] 28-32: Replace ⏎····nestedObj:·Record<string,·any>,⏎····prefix·=·'',⏎····result:·string[]·=·[],⏎·· with nestedObj:·Record<string,·any>,·prefix·=·'',·result:·string[]·=·[]

(prettier/prettier)


[error] 31-31: Unexpected trailing comma.

(comma-dangle)


49-81: Robust file reading with proper error handling.

The readJSONFilesInDir function handles both directory reading errors and individual file parsing errors gracefully, with appropriate logging.

🧰 Tools
🪛 ESLint

[error] 74-74: Delete ,

(prettier/prettier)


[error] 74-74: Unexpected trailing comma.

(comma-dangle)


83-99: Good initial setup in validateLocales.

The function properly initializes by checking for language directories and handles the case when none are found with appropriate error reporting.

🧰 Tools
🪛 ESLint

[error] 96-96: Expected blank line after variable declarations.

(newline-after-var)


[error] 97-97: Expected blank line before this statement.

(padding-line-between-statements)


101-115: Thorough language file processing.

This section effectively reads all locale files and extracts their keys for each language.

🧰 Tools
🪛 ESLint

[error] 107-107: Expected blank line after variable declarations.

(newline-after-var)


[error] 108-108: Expected blank line before this statement.

(padding-line-between-statements)


[error] 110-113: Replace ⏎··········file,⏎··········extractKeys(content),⏎········]), with file,·extractKeys(content)])

(prettier/prettier)


[error] 112-112: Unexpected trailing comma.

(comma-dangle)


[error] 113-113: Unexpected trailing comma.

(comma-dangle)


117-133: Efficient file skipping logic.

The code correctly identifies and skips files that only appear in a single language. Based on the retrieved learning, this is appropriate since Next.js handles missing file errors at runtime.

🧰 Tools
🪛 ESLint

[error] 117-119: Replace ⏎······flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files))),⏎···· with flatten(Object.values(languageFiles).map((files)·=>·Object.keys(files)))

(prettier/prettier)


[error] 118-118: Unexpected trailing comma.

(comma-dangle)


[error] 124-126: Replace ⏎········(lang)·=>·languageFiles[lang][file],⏎······ with (lang)·=>·languageFiles[lang][file]

(prettier/prettier)


[error] 125-125: Unexpected trailing comma.

(comma-dangle)


[error] 129-131: Replace ⏎··········Skipping·'${file}'·(only·found·in·${langsWithFile.length}·language),⏎········ with Skipping·'${file}'·(only·found·in·${langsWithFile.length}·language)

(prettier/prettier)


[error] 130-130: Unexpected trailing comma.

(comma-dangle)


[error] 132-132: Unexpected use of continue statement.

(no-continue)


134-163: Comprehensive missing key detection.

This section effectively identifies missing keys across languages and logs detailed error messages. The reference to hasErrors inside the forEach loop is accepted by the team based on the retrieved learnings.

🧰 Tools
🪛 ESLint

[error] 135-137: Replace ⏎········flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file])),⏎······ with flatten(langsWithFile.map((lang)·=>·fileKeys[lang][file]))

(prettier/prettier)


[error] 136-136: Unexpected trailing comma.

(comma-dangle)


[error] 142-144: Replace ⏎············(key)·=>·!langKeysSet.has(key),⏎·········· with (key)·=>·!langKeysSet.has(key)

(prettier/prettier)


[error] 143-143: Unexpected trailing comma.

(comma-dangle)


[error] 147-147: Delete ,

(prettier/prettier)


[error] 147-147: Unexpected trailing comma.

(comma-dangle)


[error] 150-152: Replace ⏎········([,·missing])·=>·missing.length·>·0,⏎······ with ([,·missing])·=>·missing.length·>·0

(prettier/prettier)


[error] 151-151: Unexpected trailing comma.

(comma-dangle)


[error] 156-161: Function declared in a loop contains unsafe references to variable(s) 'hasErrors'.

(@typescript-eslint/no-loop-func)


[error] 157-159: Replace ⏎············❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')},⏎·········· with ❌·Language·'${lang}'·is·missing·these·keys:·${missing.join(',·')}

(prettier/prettier)


[error] 158-158: Unexpected trailing comma.

(comma-dangle)


165-174: Good error handling for missing keys.

The function appropriately throws an error with a clear message when keys are missing, ensuring the validation process fails early and visibly.

🧰 Tools
🪛 ESLint

[error] 166-168: Expected blank line after variable declarations.

(newline-after-var)


[error] 166-168: Replace ⏎········'\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.',⏎······ with '\n❌·Some·translation·keys·are·missing.·Please·fix·the·issues·above.'

(prettier/prettier)


[error] 167-167: Unexpected trailing comma.

(comma-dangle)


[error] 169-169: Expected blank line before this statement.

(padding-line-between-statements)


174-178: Proper error propagation.

The function correctly catches, logs, and re-throws any errors that occur during validation.

@akshatnema
Copy link
Member

/rtm

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

Labels

bounty AsyncAPI Bounty program related label ready-to-merge

Projects

Status: Completed

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add script to have all inplace variable in each locale file

5 participants