Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: implement concurrency control for markdown file processing #3462

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

Aditya0732
Copy link

@Aditya0732 Aditya0732 commented Dec 10, 2024

Summary

Add concurrency control to markdown file validation script using p-limit to manage resource usage and improve performance.

Fixes

Changes

  • Introduced CONCURRENCY_LIMIT with a default of 10 concurrent file processing operations
  • Added support for configuring concurrency via environment variable CONCURRENCY_LIMIT
  • Implemented p-limit to control the number of concurrent file processing tasks
  • Maintained existing validation logic for docs and blog markdown files

Motivation

  • Prevent potential resource exhaustion during markdown file processing
  • Provide a configurable mechanism to control system resource usage
  • Improve script performance by limiting concurrent file operations

Implementation Details

  • Used p-limit library to manage concurrency
  • Default concurrency limit set to 10
  • Concurrency limit can be customized via CONCURRENCY_LIMIT environment variable
  • Applies to both docs and blog markdown file validation

Environment Configuration

Users can customize concurrency by setting the CONCURRENCY_LIMIT environment variable:

CONCURRENCY_LIMIT=5 node check-markdown.js

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
	- Enhanced asynchronous capabilities for markdown file checking.
	- Introduced a configurable concurrency limit for file operations.

- **Bug Fixes**
	- Improved error handling during directory reading and file validation.

- **Documentation**
	- Updated function signatures and removed outdated parameters in JSDoc comments.

- **Chores**
	- Updated project dependencies, adding `p-limit` and adjusting `fast-xml-parser`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Copy link

coderabbitai bot commented Dec 10, 2024

Walkthrough

The pull request introduces significant enhancements to the check-markdown.js script, focusing on asynchronous processing and concurrency control. The script now utilizes the promises API of the fs module, allowing for async/await syntax. A new feature limits concurrency through an environment variable CONCURRENCY_LIMIT, which defaults to 10. The checkMarkdownFiles function has been updated to read directory contents and process files asynchronously while handling errors more effectively. Additionally, some JSDoc comments have been modified to reflect changes in function signatures.

Changes

Files Change Summary
scripts/markdown/check-markdown.js Enhanced asynchronous capabilities, added concurrency control with p-limit, updated function signatures, and improved error handling. Removed certain JSDoc parameters.
package.json Added p-limit dependency, removed and re-added fast-xml-parser dependency.

Assessment against linked issues

Objective Addressed Explanation
Limit concurrency in markdown file processing to improve performance (3429)

Possibly related PRs

  • test: add markdown tests #3301: The changes in this PR involve adding tests for markdown files, which directly relates to the modifications made in check-markdown.js for validating frontmatter in markdown files.

Suggested labels

ready-to-merge, gsoc

Suggested reviewers

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

🐇 In the script where markdowns play,
Asynchronous hops now lead the way.
With limits set, we process bright,
Concurrency tamed, all feels just right!
Errors caught, the code's refined,
A leap for performance, perfectly aligned! 🐇


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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. (Experiment)
  • @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.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our contributors guide useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out this issue.

Copy link

netlify bot commented Dec 10, 2024

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 55353d0
🔍 Latest deploy log https://app.netlify.com/sites/asyncapi-website/deploys/6759a3d26751cf00088411d7
😎 Deploy Preview https://deploy-preview-3462--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.

@Aditya0732 Aditya0732 changed the title fix: Implement concurrency control for markdown file processing fix: implement concurrency control for markdown file processing Dec 10, 2024
Copy link

codecov bot commented Dec 10, 2024

Codecov Report

Attention: Patch coverage is 0% with 60 lines in your changes missing coverage. Please review.

Project coverage is 76.64%. Comparing base (7f81142) to head (55353d0).

Files with missing lines Patch % Lines
scripts/markdown/check-markdown.js 0.00% 60 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3462      +/-   ##
==========================================
- Coverage   77.22%   76.64%   -0.58%     
==========================================
  Files          21       21              
  Lines         663      668       +5     
==========================================
  Hits          512      512              
- Misses        151      156       +5     

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

@asyncapi-bot
Copy link
Contributor

asyncapi-bot commented Dec 10, 2024

⚡️ Lighthouse report for the changes in this PR:

Category Score
🔴 Performance 38
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-3462--asyncapi-website.netlify.app/

Copy link

@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

🧹 Outside diff range and nitpick comments (5)
scripts/markdown/check-markdown.js (5)

7-9: Simplify the CONCURRENCY_LIMIT assignment formatting

The ternary operator can be formatted on a single line for better readability.

Apply this diff to adjust the formatting:

-const CONCURRENCY_LIMIT = process.env.CONCURRENCY_LIMIT 
-  ? parseInt(process.env.CONCURRENCY_LIMIT, 10) 
-  : 10;
+const CONCURRENCY_LIMIT = process.env.CONCURRENCY_LIMIT ? parseInt(process.env.CONCURRENCY_LIMIT, 10) : 10;
🧰 Tools
🪛 eslint

[error] 7-9: Replace ⏎··?·parseInt(process.env.CONCURRENCY_LIMIT,·10)·⏎· with ?·parseInt(process.env.CONCURRENCY_LIMIT,·10)

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 7-7: scripts/markdown/check-markdown.js#L7
Added line #L7 was not covered by tests


Line range hint 109-148: Remove unnecessary outer try/catch block

The try/catch block from lines 109 to 148 catches filesError only to rethrow it without additional handling, which is unnecessary. Removing it simplifies the code without affecting functionality.

Apply this diff to remove the redundant try/catch block:

-    try {
         // Read directory contents
         const files = await fs.readdir(folderPath);

         // Process files with concurrency control
         const filePromises = files.map(file => limit(async () => {
             const filePath = path.join(folderPath, file);
             const relativeFilePath = path.join(relativePath, file);

             // Skip the folder 'docs/reference/specification'
             if (relativeFilePath.includes('reference/specification')) {
                 return;
             }

             try {
                 // Get file stats
                 const stats = await fs.stat(filePath);

                 // Recurse if directory, otherwise validate markdown file
                 if (stats.isDirectory()) {
                     await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
                 } else if (path.extname(file) === '.md') {
                     const fileContent = await fs.readFile(filePath, 'utf-8');
                     const { data: frontmatter } = matter(fileContent);

                     const errors = validateFunction(frontmatter);
                     if (errors) {
                         process.exitCode = 1;
                     }
                 }
             } catch (statError) {
                 throw statError;
             }
         }));
         
         // Wait for all file promises to complete
         await Promise.all(filePromises);
-    } catch (filesError) {
-        throw filesError;
-    }
🧰 Tools
🪛 Biome (1.9.4)

[error] 140-140: 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)


[error] 147-147: 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

[error] 120-120: Replace ················ with ··········

(prettier/prettier)


[error] 121-121: Delete ····

(prettier/prettier)


[error] 123-123: Delete ····

(prettier/prettier)


[error] 123-141: Unnecessary try/catch wrapper.

(no-useless-catch)


[error] 124-124: Replace ················ with ··········

(prettier/prettier)


[error] 125-125: Delete ······

(prettier/prettier)


[error] 127-127: Replace ················ with ··········

(prettier/prettier)


[error] 128-128: Delete ······

(prettier/prettier)


[error] 129-129: Replace ···················· with ············

(prettier/prettier)


[error] 130-130: Delete ······

(prettier/prettier)


[error] 131-131: Replace ···················· with ············

(prettier/prettier)


[error] 132-132: Replace ···················· with ············

(prettier/prettier)


[error] 134-134: Delete ········

(prettier/prettier)


[error] 135-135: Replace ···················· with ············

(prettier/prettier)


[error] 136-136: Replace ························ with ··············

(prettier/prettier)


[error] 137-137: Delete ········

(prettier/prettier)


[error] 138-138: Replace ················ with ··········

(prettier/prettier)


[error] 139-139: Delete ····

(prettier/prettier)


[error] 140-140: Delete ······

(prettier/prettier)


[error] 141-141: Delete ····

(prettier/prettier)


[error] 142-143: Replace ··}));⏎ with })

(prettier/prettier)


[error] 144-144: Insert );⏎⏎

(prettier/prettier)


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

(prettier/prettier)


[error] 146-146: Delete ··

(prettier/prettier)


[error] 147-147: Delete ····

(prettier/prettier)


[error] 148-148: Delete ··

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 123-123: scripts/markdown/check-markdown.js#L123
Added line #L123 was not covered by tests


[warning] 125-125: scripts/markdown/check-markdown.js#L125
Added line #L125 was not covered by tests


[warning] 129-129: scripts/markdown/check-markdown.js#L129
Added line #L129 was not covered by tests


[warning] 131-131: scripts/markdown/check-markdown.js#L131
Added line #L131 was not covered by tests


[warning] 140-140: scripts/markdown/check-markdown.js#L140
Added line #L140 was not covered by tests


[warning] 145-145: scripts/markdown/check-markdown.js#L145
Added line #L145 was not covered by tests


[warning] 147-147: scripts/markdown/check-markdown.js#L147
Added line #L147 was not covered by tests


Line range hint 105-145: Re-evaluate asynchronous file operations in recursive processing

Based on previous learnings:

"In scripts/markdown/check-markdown.js, when processing large markdown files with recursion and loops, synchronous file reads (fs.readFileSync) are preferred over asynchronous reads."

While the introduction of concurrency control with p-limit mitigates the risk of resource exhaustion, consider whether using synchronous file operations could further simplify the recursion and handling of file descriptors.

Let me know if you would like assistance in refactoring the code to use synchronous file operations.

🧰 Tools
🪛 Biome (1.9.4)

[error] 140-140: 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)


[error] 147-147: 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

[error] 120-120: Replace ················ with ··········

(prettier/prettier)


[error] 121-121: Delete ····

(prettier/prettier)


[error] 123-123: Delete ····

(prettier/prettier)


[error] 123-141: Unnecessary try/catch wrapper.

(no-useless-catch)


[error] 124-124: Replace ················ with ··········

(prettier/prettier)


[error] 125-125: Delete ······

(prettier/prettier)


[error] 127-127: Replace ················ with ··········

(prettier/prettier)


[error] 128-128: Delete ······

(prettier/prettier)


[error] 129-129: Replace ···················· with ············

(prettier/prettier)


[error] 130-130: Delete ······

(prettier/prettier)


[error] 131-131: Replace ···················· with ············

(prettier/prettier)


[error] 132-132: Replace ···················· with ············

(prettier/prettier)


[error] 134-134: Delete ········

(prettier/prettier)


[error] 135-135: Replace ···················· with ············

(prettier/prettier)


[error] 136-136: Replace ························ with ··············

(prettier/prettier)


[error] 137-137: Delete ········

(prettier/prettier)


[error] 138-138: Replace ················ with ··········

(prettier/prettier)


[error] 139-139: Delete ····

(prettier/prettier)


[error] 140-140: Delete ······

(prettier/prettier)


[error] 141-141: Delete ····

(prettier/prettier)


[error] 142-143: Replace ··}));⏎ with })

(prettier/prettier)


[error] 144-144: Insert );⏎⏎

(prettier/prettier)


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

(prettier/prettier)


[error] 146-146: Delete ··

(prettier/prettier)


[error] 147-147: Delete ····

(prettier/prettier)


[error] 148-148: Delete ··

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 123-123: scripts/markdown/check-markdown.js#L123
Added line #L123 was not covered by tests


[warning] 125-125: scripts/markdown/check-markdown.js#L125
Added line #L125 was not covered by tests


[warning] 129-129: scripts/markdown/check-markdown.js#L129
Added line #L129 was not covered by tests


[warning] 131-131: scripts/markdown/check-markdown.js#L131
Added line #L131 was not covered by tests


[warning] 140-140: scripts/markdown/check-markdown.js#L140
Added line #L140 was not covered by tests


[warning] 145-145: scripts/markdown/check-markdown.js#L145
Added line #L145 was not covered by tests


[warning] 147-147: scripts/markdown/check-markdown.js#L147
Added line #L147 was not covered by tests


Line range hint 105-148: Add unit tests for asynchronous processing and concurrency control

The new asynchronous logic and concurrency control are significant changes. Please add unit tests to cover these new code paths to maintain code coverage and ensure correct behavior.

🧰 Tools
🪛 Biome (1.9.4)

[error] 140-140: 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)


[error] 147-147: 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

[error] 120-120: Replace ················ with ··········

(prettier/prettier)


[error] 121-121: Delete ····

(prettier/prettier)


[error] 123-123: Delete ····

(prettier/prettier)


[error] 123-141: Unnecessary try/catch wrapper.

(no-useless-catch)


[error] 124-124: Replace ················ with ··········

(prettier/prettier)


[error] 125-125: Delete ······

(prettier/prettier)


[error] 127-127: Replace ················ with ··········

(prettier/prettier)


[error] 128-128: Delete ······

(prettier/prettier)


[error] 129-129: Replace ···················· with ············

(prettier/prettier)


[error] 130-130: Delete ······

(prettier/prettier)


[error] 131-131: Replace ···················· with ············

(prettier/prettier)


[error] 132-132: Replace ···················· with ············

(prettier/prettier)


[error] 134-134: Delete ········

(prettier/prettier)


[error] 135-135: Replace ···················· with ············

(prettier/prettier)


[error] 136-136: Replace ························ with ··············

(prettier/prettier)


[error] 137-137: Delete ········

(prettier/prettier)


[error] 138-138: Replace ················ with ··········

(prettier/prettier)


[error] 139-139: Delete ····

(prettier/prettier)


[error] 140-140: Delete ······

(prettier/prettier)


[error] 141-141: Delete ····

(prettier/prettier)


[error] 142-143: Replace ··}));⏎ with })

(prettier/prettier)


[error] 144-144: Insert );⏎⏎

(prettier/prettier)


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

(prettier/prettier)


[error] 146-146: Delete ··

(prettier/prettier)


[error] 147-147: Delete ····

(prettier/prettier)


[error] 148-148: Delete ··

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 123-123: scripts/markdown/check-markdown.js#L123
Added line #L123 was not covered by tests


[warning] 125-125: scripts/markdown/check-markdown.js#L125
Added line #L125 was not covered by tests


[warning] 129-129: scripts/markdown/check-markdown.js#L129
Added line #L129 was not covered by tests


[warning] 131-131: scripts/markdown/check-markdown.js#L131
Added line #L131 was not covered by tests


[warning] 140-140: scripts/markdown/check-markdown.js#L140
Added line #L140 was not covered by tests


[warning] 145-145: scripts/markdown/check-markdown.js#L145
Added line #L145 was not covered by tests


[warning] 147-147: scripts/markdown/check-markdown.js#L147
Added line #L147 was not covered by tests


Line range hint 7-148: Adjust code formatting to meet style guidelines

Several formatting inconsistencies are detected by the linter, primarily related to indentation and line breaks. Running the project's code formatter (e.g., Prettier) will resolve these issues.

🧰 Tools
🪛 Biome (1.9.4)

[error] 140-140: 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)


[error] 147-147: 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

[error] 120-120: Replace ················ with ··········

(prettier/prettier)


[error] 121-121: Delete ····

(prettier/prettier)


[error] 123-123: Delete ····

(prettier/prettier)


[error] 123-141: Unnecessary try/catch wrapper.

(no-useless-catch)


[error] 124-124: Replace ················ with ··········

(prettier/prettier)


[error] 125-125: Delete ······

(prettier/prettier)


[error] 127-127: Replace ················ with ··········

(prettier/prettier)


[error] 128-128: Delete ······

(prettier/prettier)


[error] 129-129: Replace ···················· with ············

(prettier/prettier)


[error] 130-130: Delete ······

(prettier/prettier)


[error] 131-131: Replace ···················· with ············

(prettier/prettier)


[error] 132-132: Replace ···················· with ············

(prettier/prettier)


[error] 134-134: Delete ········

(prettier/prettier)


[error] 135-135: Replace ···················· with ············

(prettier/prettier)


[error] 136-136: Replace ························ with ··············

(prettier/prettier)


[error] 137-137: Delete ········

(prettier/prettier)


[error] 138-138: Replace ················ with ··········

(prettier/prettier)


[error] 139-139: Delete ····

(prettier/prettier)


[error] 140-140: Delete ······

(prettier/prettier)


[error] 141-141: Delete ····

(prettier/prettier)


[error] 142-143: Replace ··}));⏎ with })

(prettier/prettier)


[error] 144-144: Insert );⏎⏎

(prettier/prettier)


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

(prettier/prettier)


[error] 146-146: Delete ··

(prettier/prettier)


[error] 147-147: Delete ····

(prettier/prettier)


[error] 148-148: Delete ··

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 123-123: scripts/markdown/check-markdown.js#L123
Added line #L123 was not covered by tests


[warning] 125-125: scripts/markdown/check-markdown.js#L125
Added line #L125 was not covered by tests


[warning] 129-129: scripts/markdown/check-markdown.js#L129
Added line #L129 was not covered by tests


[warning] 131-131: scripts/markdown/check-markdown.js#L131
Added line #L131 was not covered by tests


[warning] 140-140: scripts/markdown/check-markdown.js#L140
Added line #L140 was not covered by tests


[warning] 145-145: scripts/markdown/check-markdown.js#L145
Added line #L145 was not covered by tests


[warning] 147-147: scripts/markdown/check-markdown.js#L147
Added line #L147 was not covered by tests

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7f81142 and 22779e9.

📒 Files selected for processing (1)
  • scripts/markdown/check-markdown.js (3 hunks)
🧰 Additional context used
📓 Learnings (1)
scripts/markdown/check-markdown.js (1)
Learnt from: anshgoyalevil
PR: asyncapi/website#3301
File: scripts/markdown/check-markdown.js:0-0
Timestamp: 2024-11-12T06:59:33.851Z
Learning: In `scripts/markdown/check-markdown.js`, when processing large markdown files with recursion and loops, synchronous file reads (`fs.readFileSync`) are preferred over asynchronous reads.
🪛 eslint
scripts/markdown/check-markdown.js

[error] 4-4: 'p-limit' should be listed in the project's dependencies. Run 'npm i -S p-limit' to add it

(import/no-extraneous-dependencies)


[error] 7-9: Replace ⏎··?·parseInt(process.env.CONCURRENCY_LIMIT,·10)·⏎· with ?·parseInt(process.env.CONCURRENCY_LIMIT,·10)

(prettier/prettier)


[error] 106-106: Delete ··

(prettier/prettier)


[error] 107-107: Delete ··

(prettier/prettier)


[error] 109-109: Delete ··

(prettier/prettier)


[error] 110-110: Delete ····

(prettier/prettier)


[error] 111-111: Replace ········ with ····

(prettier/prettier)


[error] 113-113: Delete ····

(prettier/prettier)


[error] 114-114: Replace ····const·filePromises·=·files.map(file·=> with const·filePromises·=·files.map((file)·=>⏎·····

(prettier/prettier)


[error] 109-148: Unnecessary try/catch wrapper.

(no-useless-catch)


[error] 123-123: Delete ····

(prettier/prettier)


[error] 123-141: Unnecessary try/catch wrapper.

(no-useless-catch)


[error] 124-124: Replace ················ with ··········

(prettier/prettier)


[error] 125-125: Delete ······

(prettier/prettier)


[error] 127-127: Replace ················ with ··········

(prettier/prettier)


[error] 128-128: Delete ······

(prettier/prettier)


[error] 129-129: Replace ···················· with ············

(prettier/prettier)


[error] 130-130: Delete ······

(prettier/prettier)


[error] 131-131: Replace ···················· with ············

(prettier/prettier)


[error] 132-132: Replace ···················· with ············

(prettier/prettier)


[error] 134-134: Delete ········

(prettier/prettier)


[error] 135-135: Replace ···················· with ············

(prettier/prettier)


[error] 136-136: Replace ························ with ··············

(prettier/prettier)


[error] 137-137: Delete ········

(prettier/prettier)


[error] 138-138: Replace ················ with ··········

(prettier/prettier)


[error] 139-139: Delete ····

(prettier/prettier)


[error] 140-140: Delete ······

(prettier/prettier)


[error] 141-141: Delete ····

(prettier/prettier)


[error] 142-143: Replace ··}));⏎ with })

(prettier/prettier)


[error] 144-144: Insert );⏎⏎

(prettier/prettier)


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

(prettier/prettier)


[error] 146-146: Delete ··

(prettier/prettier)


[error] 147-147: Delete ····

(prettier/prettier)


[error] 148-148: Delete ··

(prettier/prettier)

🪛 GitHub Check: codecov/patch
scripts/markdown/check-markdown.js

[warning] 1-1: scripts/markdown/check-markdown.js#L1
Added line #L1 was not covered by tests


[warning] 4-4: scripts/markdown/check-markdown.js#L4
Added line #L4 was not covered by tests


[warning] 7-7: scripts/markdown/check-markdown.js#L7
Added line #L7 was not covered by tests


[warning] 107-107: scripts/markdown/check-markdown.js#L107
Added line #L107 was not covered by tests


[warning] 109-109: scripts/markdown/check-markdown.js#L109
Added line #L109 was not covered by tests


[warning] 111-111: scripts/markdown/check-markdown.js#L111
Added line #L111 was not covered by tests


[warning] 114-114: scripts/markdown/check-markdown.js#L114
Added line #L114 was not covered by tests


[warning] 123-123: scripts/markdown/check-markdown.js#L123
Added line #L123 was not covered by tests


[warning] 125-125: scripts/markdown/check-markdown.js#L125
Added line #L125 was not covered by tests


[warning] 129-129: scripts/markdown/check-markdown.js#L129
Added line #L129 was not covered by tests


[warning] 131-131: scripts/markdown/check-markdown.js#L131
Added line #L131 was not covered by tests


[warning] 140-140: scripts/markdown/check-markdown.js#L140
Added line #L140 was not covered by tests


[warning] 145-145: scripts/markdown/check-markdown.js#L145
Added line #L145 was not covered by tests


[warning] 147-147: scripts/markdown/check-markdown.js#L147
Added line #L147 was not covered by tests

🪛 Biome (1.9.4)
scripts/markdown/check-markdown.js

[error] 140-140: 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)


[error] 147-147: 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)

scripts/markdown/check-markdown.js Show resolved Hide resolved
Comment on lines 123 to 141
try {
// Get file stats
const stats = await fs.stat(filePath);

// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);

const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach(error => console.log(` - ${error}`));
process.exitCode = 1;
}
}
});
});
});
} catch (statError) {
throw statError;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unnecessary inner try/catch block

The try/catch block from lines 123 to 141 is unnecessary because it only rethrows the caught error without additional processing. Eliminating it will make the code cleaner.

Apply this diff to remove the unnecessary try/catch block:

-            try {
                 // Get file stats
                 const stats = await fs.stat(filePath);

                 // Recurse if directory, otherwise validate markdown file
                 if (stats.isDirectory()) {
                     await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
                 } else if (path.extname(file) === '.md') {
                     const fileContent = await fs.readFile(filePath, 'utf-8');
                     const { data: frontmatter } = matter(fileContent);

                     const errors = validateFunction(frontmatter);
                     if (errors) {
                         process.exitCode = 1;
                     }
                 }
-            } catch (statError) {
-                throw statError;
-            }
📝 Committable suggestion

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

Suggested change
try {
// Get file stats
const stats = await fs.stat(filePath);
// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);
const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach(error => console.log(` - ${error}`));
process.exitCode = 1;
}
}
});
});
});
} catch (statError) {
throw statError;
}
// Get file stats
const stats = await fs.stat(filePath);
// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);
const errors = validateFunction(frontmatter);
if (errors) {
process.exitCode = 1;
}
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 140-140: 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

[error] 123-123: Delete ····

(prettier/prettier)


[error] 123-141: Unnecessary try/catch wrapper.

(no-useless-catch)


[error] 124-124: Replace ················ with ··········

(prettier/prettier)


[error] 125-125: Delete ······

(prettier/prettier)


[error] 127-127: Replace ················ with ··········

(prettier/prettier)


[error] 128-128: Delete ······

(prettier/prettier)


[error] 129-129: Replace ···················· with ············

(prettier/prettier)


[error] 130-130: Delete ······

(prettier/prettier)


[error] 131-131: Replace ···················· with ············

(prettier/prettier)


[error] 132-132: Replace ···················· with ············

(prettier/prettier)


[error] 134-134: Delete ········

(prettier/prettier)


[error] 135-135: Replace ···················· with ············

(prettier/prettier)


[error] 136-136: Replace ························ with ··············

(prettier/prettier)


[error] 137-137: Delete ········

(prettier/prettier)


[error] 138-138: Replace ················ with ··········

(prettier/prettier)


[error] 139-139: Delete ····

(prettier/prettier)


[error] 140-140: Delete ······

(prettier/prettier)


[error] 141-141: Delete ····

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 123-123: scripts/markdown/check-markdown.js#L123
Added line #L123 was not covered by tests


[warning] 125-125: scripts/markdown/check-markdown.js#L125
Added line #L125 was not covered by tests


[warning] 129-129: scripts/markdown/check-markdown.js#L129
Added line #L129 was not covered by tests


[warning] 131-131: scripts/markdown/check-markdown.js#L131
Added line #L131 was not covered by tests


[warning] 140-140: scripts/markdown/check-markdown.js#L140
Added line #L140 was not covered by tests

Copy link

@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

🧹 Outside diff range and nitpick comments (3)
scripts/markdown/check-markdown.js (3)

6-7: Consider adding validation for CONCURRENCY_LIMIT.

While the default of 10 is reasonable, the environment variable should be validated to ensure it's a positive integer.

 // Configuration
-const CONCURRENCY_LIMIT = process.env.CONCURRENCY_LIMIT ? parseInt(process.env.CONCURRENCY_LIMIT, 10) : 10;
+const parsedLimit = process.env.CONCURRENCY_LIMIT ? parseInt(process.env.CONCURRENCY_LIMIT, 10) : 10;
+const CONCURRENCY_LIMIT = Number.isInteger(parsedLimit) && parsedLimit > 0 ? parsedLimit : 10;
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 7-7: scripts/markdown/check-markdown.js#L7
Added line #L7 was not covered by tests


15-20: Avoid side effects in URL validation.

The current implementation creates a new URL object just for validation. Consider using the URL constructor's return value instead.

 function isValidURL(str) {
   try {
-    new URL(str);
-    return true;
+    return !!new URL(str);
   } catch (err) {
     return false;
   }
 }
🧰 Tools
🪛 eslint

[error] 16-16: Do not use 'new' for side effects.

(no-new)

🪛 GitHub Check: codecov/patch

[warning] 15-17: scripts/markdown/check-markdown.js#L15-L17
Added lines #L15 - L17 were not covered by tests


[warning] 19-19: scripts/markdown/check-markdown.js#L19
Added line #L19 was not covered by tests


135-136: Document the reason for skipping 'reference/specification'.

The code skips the 'reference/specification' directory without explanation. Consider adding a comment explaining why this directory is excluded.

         // Skip specific folder
+        // Skip reference/specification as it contains auto-generated markdown files
         if (relativeFilePath.includes('reference/specification')) {
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 135-136: scripts/markdown/check-markdown.js#L135-L136
Added lines #L135 - L136 were not covered by tests

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 22779e9 and d4b5107.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json (2 hunks)
  • scripts/markdown/check-markdown.js (1 hunks)
🧰 Additional context used
📓 Learnings (1)
scripts/markdown/check-markdown.js (1)
Learnt from: anshgoyalevil
PR: asyncapi/website#3301
File: scripts/markdown/check-markdown.js:0-0
Timestamp: 2024-11-12T06:59:33.851Z
Learning: In `scripts/markdown/check-markdown.js`, when processing large markdown files with recursion and loops, synchronous file reads (`fs.readFileSync`) are preferred over asynchronous reads.
🪛 Biome (1.9.4)
scripts/markdown/check-markdown.js

[error] 41-41: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🪛 eslint
scripts/markdown/check-markdown.js

[error] 16-16: Do not use 'new' for side effects.

(no-new)


[error] 29-36: Replace ⏎····'title',⏎····'date',⏎····'type',⏎····'tags',⏎····'cover',⏎····'authors',⏎·· with 'title',·'date',·'type',·'tags',·'cover',·'authors'

(prettier/prettier)


[error] 41-41: Do not access Object.prototype method 'hasOwnProperty' from target object.

(no-prototype-builtins)


[error] 71-73: Replace ⏎············Invalid·URL·for·author·at·index·${index}:·${author.link},⏎·········· with Invalid·URL·for·author·at·index·${index}:·${author.link}

(prettier/prettier)


[error] 99-102: Replace ⏎····frontmatter.weight·===·undefined·||⏎····typeof·frontmatter.weight·!==·'number'⏎·· with frontmatter.weight·===·undefined·||·typeof·frontmatter.weight·!==·'number'

(prettier/prettier)


[error] 116-120: Replace ⏎··folderPath,⏎··validateFunction,⏎··relativePath·=·'',⏎ with folderPath,·validateFunction,·relativePath·=·''

(prettier/prettier)


[error] 144-148: Replace ⏎············filePath,⏎············validateFunction,⏎············relativeFilePath,⏎·········· with filePath,·validateFunction,·relativeFilePath

(prettier/prettier)


[error] 160-162: Replace ⏎··············Error·reading·file·${relativeFilePath}:·${readError.message},⏎············ with Error·reading·file·${relativeFilePath}:·${readError.message}

(prettier/prettier)


[error] 167-167: Delete ,

(prettier/prettier)

🪛 GitHub Check: codecov/patch
scripts/markdown/check-markdown.js

[warning] 3-4: scripts/markdown/check-markdown.js#L3-L4
Added lines #L3 - L4 were not covered by tests


[warning] 7-7: scripts/markdown/check-markdown.js#L7
Added line #L7 was not covered by tests


[warning] 15-17: scripts/markdown/check-markdown.js#L15-L17
Added lines #L15 - L17 were not covered by tests


[warning] 19-19: scripts/markdown/check-markdown.js#L19
Added line #L19 was not covered by tests


[warning] 29-29: scripts/markdown/check-markdown.js#L29
Added line #L29 was not covered by tests


[warning] 37-37: scripts/markdown/check-markdown.js#L37
Added line #L37 was not covered by tests


[warning] 40-42: scripts/markdown/check-markdown.js#L40-L42
Added lines #L40 - L42 were not covered by tests


[warning] 47-48: scripts/markdown/check-markdown.js#L47-L48
Added lines #L47 - L48 were not covered by tests


[warning] 52-53: scripts/markdown/check-markdown.js#L52-L53
Added lines #L52 - L53 were not covered by tests


[warning] 57-58: scripts/markdown/check-markdown.js#L57-L58
Added lines #L57 - L58 were not covered by tests


[warning] 62-64: scripts/markdown/check-markdown.js#L62-L64
Added lines #L62 - L64 were not covered by tests


[warning] 66-68: scripts/markdown/check-markdown.js#L66-L68
Added lines #L66 - L68 were not covered by tests


[warning] 70-71: scripts/markdown/check-markdown.js#L70-L71
Added lines #L70 - L71 were not covered by tests


[warning] 75-76: scripts/markdown/check-markdown.js#L75-L76
Added lines #L75 - L76 were not covered by tests


[warning] 82-82: scripts/markdown/check-markdown.js#L82
Added line #L82 was not covered by tests


[warning] 91-91: scripts/markdown/check-markdown.js#L91
Added line #L91 was not covered by tests


[warning] 94-95: scripts/markdown/check-markdown.js#L94-L95
Added lines #L94 - L95 were not covered by tests


[warning] 99-99: scripts/markdown/check-markdown.js#L99
Added line #L99 was not covered by tests


[warning] 103-103: scripts/markdown/check-markdown.js#L103
Added line #L103 was not covered by tests


[warning] 106-106: scripts/markdown/check-markdown.js#L106
Added line #L106 was not covered by tests


[warning] 121-122: scripts/markdown/check-markdown.js#L121-L122
Added lines #L121 - L122 were not covered by tests


[warning] 124-124: scripts/markdown/check-markdown.js#L124
Added line #L124 was not covered by tests


[warning] 126-126: scripts/markdown/check-markdown.js#L126
Added line #L126 was not covered by tests


[warning] 129-132: scripts/markdown/check-markdown.js#L129-L132
Added lines #L129 - L132 were not covered by tests


[warning] 135-136: scripts/markdown/check-markdown.js#L135-L136
Added lines #L135 - L136 were not covered by tests


[warning] 140-140: scripts/markdown/check-markdown.js#L140
Added line #L140 was not covered by tests


[warning] 143-144: scripts/markdown/check-markdown.js#L143-L144
Added lines #L143 - L144 were not covered by tests


[warning] 149-153: scripts/markdown/check-markdown.js#L149-L153
Added lines #L149 - L153 were not covered by tests


[warning] 155-157: scripts/markdown/check-markdown.js#L155-L157
Added lines #L155 - L157 were not covered by tests


[warning] 160-160: scripts/markdown/check-markdown.js#L160
Added line #L160 was not covered by tests

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

93-93: LGTM! The addition of p-limit is appropriate.

The addition of p-limit aligns with the PR's objective of implementing concurrency control for markdown file processing.

scripts/markdown/check-markdown.js (2)

121-122: LGTM! Effective concurrency control implementation.

The use of p-limit with Promise.all effectively manages concurrent file operations while maintaining control over resource usage.

Also applies to: 170-171

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 121-122: scripts/markdown/check-markdown.js#L121-L122
Added lines #L121 - L122 were not covered by tests


151-163: 🛠️ Refactor suggestion

Consider using synchronous file operations consistently.

Based on the retrieved learning, synchronous file operations are preferred for processing large markdown files. The code already uses readdirSync and statSync, but mixes async/await with synchronous operations.

🧰 Tools
🪛 eslint

[error] 160-162: Replace ⏎··············Error·reading·file·${relativeFilePath}:·${readError.message},⏎············ with Error·reading·file·${relativeFilePath}:·${readError.message}

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 155-157: scripts/markdown/check-markdown.js#L155-L157
Added lines #L155 - L157 were not covered by tests


[warning] 160-160: scripts/markdown/check-markdown.js#L160
Added line #L160 was not covered by tests

Comment on lines +41 to +42
if (!frontmatter.hasOwnProperty(attr)) {
errors.push(`${attr} is missing`);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use Object.hasOwn() instead of hasOwnProperty.

Direct use of hasOwnProperty is discouraged as it can be overridden. Use Object.hasOwn() for safer property checks.

-    if (!frontmatter.hasOwnProperty(attr)) {
+    if (!Object.hasOwn(frontmatter, attr)) {
📝 Committable suggestion

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

Suggested change
if (!frontmatter.hasOwnProperty(attr)) {
errors.push(`${attr} is missing`);
if (!Object.hasOwn(frontmatter, attr)) {
errors.push(`${attr} is missing`);
🧰 Tools
🪛 Biome (1.9.4)

[error] 41-41: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🪛 eslint

[error] 41-41: Do not access Object.prototype method 'hasOwnProperty' from target object.

(no-prototype-builtins)

🪛 GitHub Check: codecov/patch

[warning] 40-42: scripts/markdown/check-markdown.js#L40-L42
Added lines #L40 - L42 were not covered by tests

scripts/markdown/check-markdown.js Outdated Show resolved Hide resolved
Copy link

@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: 3

🧹 Outside diff range and nitpick comments (2)
scripts/markdown/check-markdown.js (2)

111-111: Consider batching file operations for better performance

The current implementation processes files one by one with concurrency control. Consider batching file operations to reduce overhead:

-const filePromises = files.map((file) =>
-    limit(async () => {
-        const filePath = path.join(folderPath, file);
-        const relativeFilePath = path.join(relativePath, file);
+const batchSize = 5;
+const filePromises = [];
+for (let i = 0; i < files.length; i += batchSize) {
+    const batch = files.slice(i, i + batchSize);
+    filePromises.push(
+        limit(async () => {
+            const results = await Promise.all(
+                batch.map(async (file) => {
+                    const filePath = path.join(folderPath, file);
+                    const relativeFilePath = path.join(relativePath, file);

Also applies to: 119-122

🧰 Tools
🪛 eslint

[error] 111-111: Delete ··

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 111-112: scripts/markdown/check-markdown.js#L111-L112
Added lines #L111 - L112 were not covered by tests


125-126: Document the reference/specification exclusion

The code skips the 'reference/specification' folder without explanation. Consider adding a comment explaining why this folder is excluded.

+// Skip auto-generated specification documentation
 if (relativeFilePath.includes('reference/specification')) {
     return [];
 }
🧰 Tools
🪛 eslint

[error] 125-125: Replace ················ with ········

(prettier/prettier)


[error] 126-126: Replace ···················· with ··········

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 125-126: scripts/markdown/check-markdown.js#L125-L126
Added lines #L125 - L126 were not covered by tests

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d4b5107 and 86dbaad.

📒 Files selected for processing (1)
  • scripts/markdown/check-markdown.js (2 hunks)
🧰 Additional context used
📓 Learnings (1)
scripts/markdown/check-markdown.js (2)
Learnt from: anshgoyalevil
PR: asyncapi/website#3301
File: scripts/markdown/check-markdown.js:31-31
Timestamp: 2024-11-12T06:59:33.852Z
Learning: In `scripts/markdown/check-markdown.js`, the `frontmatter` object cannot have a property named `hasOwnProperty`, so using `frontmatter.hasOwnProperty(attr)` is acceptable.
Learnt from: anshgoyalevil
PR: asyncapi/website#3301
File: scripts/markdown/check-markdown.js:0-0
Timestamp: 2024-11-12T06:59:33.851Z
Learning: In `scripts/markdown/check-markdown.js`, when processing large markdown files with recursion and loops, synchronous file reads (`fs.readFileSync`) are preferred over asynchronous reads.
🪛 GitHub Check: codecov/patch
scripts/markdown/check-markdown.js

[warning] 3-4: scripts/markdown/check-markdown.js#L3-L4
Added lines #L3 - L4 were not covered by tests


[warning] 7-8: scripts/markdown/check-markdown.js#L7-L8
Added lines #L7 - L8 were not covered by tests


[warning] 111-112: scripts/markdown/check-markdown.js#L111-L112
Added lines #L111 - L112 were not covered by tests


[warning] 114-114: scripts/markdown/check-markdown.js#L114
Added line #L114 was not covered by tests


[warning] 116-116: scripts/markdown/check-markdown.js#L116
Added line #L116 was not covered by tests


[warning] 119-122: scripts/markdown/check-markdown.js#L119-L122
Added lines #L119 - L122 were not covered by tests


[warning] 125-126: scripts/markdown/check-markdown.js#L125-L126
Added lines #L125 - L126 were not covered by tests


[warning] 130-130: scripts/markdown/check-markdown.js#L130
Added line #L130 was not covered by tests


[warning] 134-134: scripts/markdown/check-markdown.js#L134
Added line #L134 was not covered by tests


[warning] 139-139: scripts/markdown/check-markdown.js#L139
Added line #L139 was not covered by tests


[warning] 141-143: scripts/markdown/check-markdown.js#L141-L143
Added lines #L141 - L143 were not covered by tests


[warning] 145-148: scripts/markdown/check-markdown.js#L145-L148
Added lines #L145 - L148 were not covered by tests


[warning] 151-151: scripts/markdown/check-markdown.js#L151
Added line #L151 was not covered by tests


[warning] 157-157: scripts/markdown/check-markdown.js#L157
Added line #L157 was not covered by tests


[warning] 162-162: scripts/markdown/check-markdown.js#L162
Added line #L162 was not covered by tests


[warning] 164-164: scripts/markdown/check-markdown.js#L164
Added line #L164 was not covered by tests


[warning] 166-167: scripts/markdown/check-markdown.js#L166-L167
Added lines #L166 - L167 were not covered by tests


[warning] 176-177: scripts/markdown/check-markdown.js#L176-L177
Added lines #L176 - L177 were not covered by tests


[warning] 179-179: scripts/markdown/check-markdown.js#L179
Added line #L179 was not covered by tests


[warning] 181-185: scripts/markdown/check-markdown.js#L181-L185
Added lines #L181 - L185 were not covered by tests


[warning] 189-193: scripts/markdown/check-markdown.js#L189-L193
Added lines #L189 - L193 were not covered by tests


[warning] 196-196: scripts/markdown/check-markdown.js#L196
Added line #L196 was not covered by tests


[warning] 198-199: scripts/markdown/check-markdown.js#L198-L199
Added lines #L198 - L199 were not covered by tests


[warning] 204-204: scripts/markdown/check-markdown.js#L204
Added line #L204 was not covered by tests

🪛 eslint
scripts/markdown/check-markdown.js

[error] 106-110: Replace ⏎····folderPath,⏎····validateFunction,⏎····relativePath·=·'',⏎ with folderPath,·validateFunction,·relativePath·=·''

(prettier/prettier)


[error] 111-111: Delete ··

(prettier/prettier)


[error] 112-112: Delete ··

(prettier/prettier)


[error] 114-114: Delete ··

(prettier/prettier)


[error] 115-115: Delete ····

(prettier/prettier)


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

(prettier/prettier)


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

(prettier/prettier)


[error] 119-119: Delete ····

(prettier/prettier)


[error] 120-120: Delete ······

(prettier/prettier)


[error] 121-121: Replace ················ with ········

(prettier/prettier)


[error] 122-122: Replace ················ with ········

(prettier/prettier)


[error] 124-124: Delete ········

(prettier/prettier)


[error] 125-125: Replace ················ with ········

(prettier/prettier)


[error] 126-126: Replace ···················· with ··········

(prettier/prettier)


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

(prettier/prettier)


[error] 129-129: Replace ················ with ········

(prettier/prettier)


[error] 130-130: Replace ················ with ········

(prettier/prettier)


[error] 132-132: Delete ········

(prettier/prettier)


[error] 133-133: Replace ················ with ········

(prettier/prettier)


[error] 134-138: Replace ····················const·subdirErrors·=·await·checkMarkdownFiles(⏎························filePath,⏎························validateFunction,⏎························relativeFilePath,⏎···················· with ··········const·subdirErrors·=·await·checkMarkdownFiles(filePath,·validateFunction,·relativeFilePath

(prettier/prettier)


[error] 139-139: Delete ··········

(prettier/prettier)


[error] 140-140: Replace ················ with ········

(prettier/prettier)


[error] 141-141: Replace ···················· with ··········

(prettier/prettier)


[error] 142-142: Delete ············

(prettier/prettier)


[error] 143-143: Replace ························ with ············

(prettier/prettier)


[error] 145-145: Replace ························ with ············

(prettier/prettier)


[error] 146-146: Replace ························ with ············

(prettier/prettier)


[error] 147-147: Delete ··············

(prettier/prettier)


[error] 148-148: Replace ···························· with ··············

(prettier/prettier)


[error] 149-149: Delete ············

(prettier/prettier)


[error] 150-150: Replace ···················· with ··········

(prettier/prettier)


[error] 151-153: Replace ························allErrors.push(⏎····························Error·reading·file·${relativeFilePath}:·${readError.message},⏎························ with ············allErrors.push(Error·reading·file·${relativeFilePath}:·${readError.message}``

(prettier/prettier)


[error] 154-154: Replace ···················· with ··········

(prettier/prettier)


[error] 155-155: Replace ················ with ········

(prettier/prettier)


[error] 157-157: Delete ········

(prettier/prettier)


[error] 158-158: Replace ············}), with ······})

(prettier/prettier)


[error] 159-159: Replace ········ with ····

(prettier/prettier)


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

(prettier/prettier)


[error] 162-162: Delete ····

(prettier/prettier)


[error] 164-164: Replace ········ with ····

(prettier/prettier)


[error] 165-165: Replace ···· with ··

(prettier/prettier)


[error] 166-166: Replace ········ with ····

(prettier/prettier)


[error] 167-167: Delete ····

(prettier/prettier)


[error] 168-168: Replace ···· with ··

(prettier/prettier)


[error] 176-176: Delete ··

(prettier/prettier)


[error] 177-177: Delete ··

(prettier/prettier)


[error] 179-179: Replace ···· with ··

(prettier/prettier)


[error] 180-180: Delete ····

(prettier/prettier)


[error] 181-181: Delete ····

(prettier/prettier)


[error] 182-182: Replace ········ with ····

(prettier/prettier)


[error] 183-183: Delete ······

(prettier/prettier)


[error] 184-184: Replace ············ with ······

(prettier/prettier)


[error] 185-185: Delete ······

(prettier/prettier)


[error] 186-186: Delete ····

(prettier/prettier)


[error] 188-188: Replace ········ with ····

(prettier/prettier)


[error] 189-189: Delete ····

(prettier/prettier)


[error] 190-190: Delete ····

(prettier/prettier)


[error] 191-191: Replace ············ with ······

(prettier/prettier)


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

(prettier/prettier)


[error] 193-193: Replace ············ with ······

(prettier/prettier)


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

(prettier/prettier)


[error] 196-196: Delete ····

(prettier/prettier)


[error] 197-197: Delete ··

(prettier/prettier)


[error] 198-198: Delete ····

(prettier/prettier)


[error] 199-199: Delete ····

(prettier/prettier)


[error] 200-200: Delete ··

(prettier/prettier)

🔇 Additional comments (2)
scripts/markdown/check-markdown.js (2)

4-4: Add 'p-limit' to the project's dependencies

The p-limit library is being used but is not listed in the project's dependencies.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 3-4: scripts/markdown/check-markdown.js#L3-L4
Added lines #L3 - L4 were not covered by tests


7-8: LGTM: Robust CONCURRENCY_LIMIT implementation

The implementation includes proper parsing, validation, and fallback handling for the concurrency limit configuration.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 7-8: scripts/markdown/check-markdown.js#L7-L8
Added lines #L7 - L8 were not covered by tests

return;
try {
// Read directory contents synchronously for predictability
const files = fs.readdirSync(folderPath);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using async file operations consistently

The implementation mixes synchronous (readdirSync, statSync, readFileSync) and asynchronous operations. This can impact performance and block the event loop. Consider using the async versions consistently:

-const files = fs.readdirSync(folderPath);
+const files = await fs.promises.readdir(folderPath);

-const stats = fs.statSync(filePath);
+const stats = await fs.promises.stat(filePath);

-const fileContent = fs.readFileSync(filePath, 'utf-8');
+const fileContent = await fs.promises.readFile(filePath, 'utf-8');

Also applies to: 130-130, 142-142

🧰 Tools
🪛 eslint

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

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 116-116: scripts/markdown/check-markdown.js#L116
Added line #L116 was not covered by tests

Comment on lines 145 to 149
const errors = validateFunction(frontmatter);
if (errors.length > 0) {
console.log(`Errors in file ${relativeFilePath}:`);
allErrors.push(`File ${relativeFilePath}: ${errors.join('; ')}`);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling consistency

The validation functions return null when there are no errors, but the code assumes an array is returned. This can lead to runtime errors.

-if (errors.length > 0) {
+if (errors && errors.length > 0) {

Also, consider collecting all errors before exiting:

-if (errors && errors.length > 0) {
-    console.log(`Errors in file ${relativeFilePath}:`);
-    allErrors.push(`File ${relativeFilePath}: ${errors.join('; ')}`);
-}
+if (errors) {
+    const errorMessage = `File ${relativeFilePath}: ${errors.join('; ')}`;
+    console.log('Errors in file:', errorMessage);
+    allErrors.push(errorMessage);
+}

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 eslint

[error] 145-145: Replace ························ with ············

(prettier/prettier)


[error] 146-146: Replace ························ with ············

(prettier/prettier)


[error] 147-147: Delete ··············

(prettier/prettier)


[error] 148-148: Replace ···························· with ··············

(prettier/prettier)


[error] 149-149: Delete ············

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 145-148: scripts/markdown/check-markdown.js#L145-L148
Added lines #L145 - L148 were not covered by tests

Comment on lines 106 to 168
async function checkMarkdownFiles(
folderPath,
validateFunction,
relativePath = '',
) {
const limit = pLimit(CONCURRENCY_LIMIT);
const allErrors = [];

fs.stat(filePath, (err, stats) => {
if (err) {
console.error('Error reading file stats:', err);
return;
try {
// Read directory contents synchronously for predictability
const files = fs.readdirSync(folderPath);

// Process files with concurrency control
const filePromises = files.map((file) =>
limit(async () => {
const filePath = path.join(folderPath, file);
const relativeFilePath = path.join(relativePath, file);

// Skip the folder 'docs/reference/specification'
if (relativeFilePath.includes('reference/specification')) {
return [];
}

// Get file stats
const stats = fs.statSync(filePath);

// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
const subdirErrors = await checkMarkdownFiles(
filePath,
validateFunction,
relativeFilePath,
);
allErrors.push(...subdirErrors);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);

const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach(error => console.log(` - ${error}`));
process.exitCode = 1;
try {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);

const errors = validateFunction(frontmatter);
if (errors.length > 0) {
console.log(`Errors in file ${relativeFilePath}:`);
allErrors.push(`File ${relativeFilePath}: ${errors.join('; ')}`);
}
} catch (readError) {
allErrors.push(
`Error reading file ${relativeFilePath}: ${readError.message}`,
);
}
}
});
});
});

return [];
}),
);

// Wait for all file promises to complete
await Promise.all(filePromises);

return allErrors;
} catch (error) {
console.error('Error processing files:', error);
return [`Folder processing error: ${error.message}`];
}
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the gathered information, I can now provide the final response:

Test coverage needed for markdown validation functionality

The check-markdown.js script contains critical validation logic but lacks test coverage. Key areas requiring tests:

  • validateBlogs function:

    • Required attributes validation
    • Date format validation
    • Tags array validation
    • Authors array validation
    • URL validation for author links
  • validateDocs function:

    • Title validation
    • Weight validation
  • checkMarkdownFiles function:

    • Directory traversal
    • Concurrent file processing
    • Error handling for invalid files
    • Skipping 'reference/specification' folder

Suggested test setup:

  1. Mock fs and gray-matter modules
  2. Create sample frontmatter fixtures
  3. Test both success and error scenarios
  4. Verify concurrency control with pLimit
🔗 Analysis chain

Add test coverage for the validation process

The codecov report indicates missing test coverage for the validation logic. Consider adding unit tests to verify:

  • Validation of frontmatter attributes
  • Error handling for invalid files
  • Concurrency control behavior

Would you like me to help create unit tests for this functionality?

Also applies to: 171-201


Let me check the content of test files to see if there's any existing test coverage for the markdown validation functionality.


Let me check if the markdown validation functions are exported and how they're used in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for existing test files
fd -e test.js -e spec.js . tests/ scripts/

# Search for markdown validation related tests
rg -l "validateMarkdown|checkMarkdownFiles" tests/

# Look for any test utilities or helpers
rg -l "jest.mock|describe.*markdown" tests/

Length of output: 1029


Script:

#!/bin/bash
# Check content of test files for any markdown validation related tests
rg -A 5 "describe.*markdown|test.*markdown|validateMarkdown|checkMarkdownFiles" tests/

# Look for any mock implementations of fs or path modules in tests
rg -A 5 "jest.mock.*('fs')|jest.mock.*('path')" tests/

# Check the implementation file for any exported functions that might be tested
rg -l "module.exports.*checkMarkdownFiles|module.exports.*validateMarkdown" scripts/

Length of output: 530


Script:

#!/bin/bash
# Check the content of the markdown validation file
cat scripts/markdown/check-markdown.js

# Find any imports or usage of these validation functions
rg -l "require.*check-markdown|import.*check-markdown" .

# Look for any test setup files that might contain markdown-related mocks
fd -e js . tests/__mocks__/ tests/setup/ 2>/dev/null || true

Length of output: 7385

🧰 Tools
🪛 eslint

[error] 106-110: Replace ⏎····folderPath,⏎····validateFunction,⏎····relativePath·=·'',⏎ with folderPath,·validateFunction,·relativePath·=·''

(prettier/prettier)


[error] 111-111: Delete ··

(prettier/prettier)


[error] 112-112: Delete ··

(prettier/prettier)


[error] 114-114: Delete ··

(prettier/prettier)


[error] 115-115: Delete ····

(prettier/prettier)


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

(prettier/prettier)


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

(prettier/prettier)


[error] 119-119: Delete ····

(prettier/prettier)


[error] 120-120: Delete ······

(prettier/prettier)


[error] 121-121: Replace ················ with ········

(prettier/prettier)


[error] 122-122: Replace ················ with ········

(prettier/prettier)


[error] 124-124: Delete ········

(prettier/prettier)


[error] 125-125: Replace ················ with ········

(prettier/prettier)


[error] 126-126: Replace ···················· with ··········

(prettier/prettier)


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

(prettier/prettier)


[error] 129-129: Replace ················ with ········

(prettier/prettier)


[error] 130-130: Replace ················ with ········

(prettier/prettier)


[error] 132-132: Delete ········

(prettier/prettier)


[error] 133-133: Replace ················ with ········

(prettier/prettier)


[error] 134-138: Replace ····················const·subdirErrors·=·await·checkMarkdownFiles(⏎························filePath,⏎························validateFunction,⏎························relativeFilePath,⏎···················· with ··········const·subdirErrors·=·await·checkMarkdownFiles(filePath,·validateFunction,·relativeFilePath

(prettier/prettier)


[error] 139-139: Delete ··········

(prettier/prettier)


[error] 140-140: Replace ················ with ········

(prettier/prettier)


[error] 141-141: Replace ···················· with ··········

(prettier/prettier)


[error] 142-142: Delete ············

(prettier/prettier)


[error] 143-143: Replace ························ with ············

(prettier/prettier)


[error] 145-145: Replace ························ with ············

(prettier/prettier)


[error] 146-146: Replace ························ with ············

(prettier/prettier)


[error] 147-147: Delete ··············

(prettier/prettier)


[error] 148-148: Replace ···························· with ··············

(prettier/prettier)


[error] 149-149: Delete ············

(prettier/prettier)


[error] 150-150: Replace ···················· with ··········

(prettier/prettier)


[error] 151-153: Replace ························allErrors.push(⏎····························Error·reading·file·${relativeFilePath}:·${readError.message},⏎························ with ············allErrors.push(Error·reading·file·${relativeFilePath}:·${readError.message}``

(prettier/prettier)


[error] 154-154: Replace ···················· with ··········

(prettier/prettier)


[error] 155-155: Replace ················ with ········

(prettier/prettier)


[error] 157-157: Delete ········

(prettier/prettier)


[error] 158-158: Replace ············}), with ······})

(prettier/prettier)


[error] 159-159: Replace ········ with ····

(prettier/prettier)


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

(prettier/prettier)


[error] 162-162: Delete ····

(prettier/prettier)


[error] 164-164: Replace ········ with ····

(prettier/prettier)


[error] 165-165: Replace ···· with ··

(prettier/prettier)


[error] 166-166: Replace ········ with ····

(prettier/prettier)


[error] 167-167: Delete ····

(prettier/prettier)


[error] 168-168: Replace ···· with ··

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 111-112: scripts/markdown/check-markdown.js#L111-L112
Added lines #L111 - L112 were not covered by tests


[warning] 114-114: scripts/markdown/check-markdown.js#L114
Added line #L114 was not covered by tests


[warning] 116-116: scripts/markdown/check-markdown.js#L116
Added line #L116 was not covered by tests


[warning] 119-122: scripts/markdown/check-markdown.js#L119-L122
Added lines #L119 - L122 were not covered by tests


[warning] 125-126: scripts/markdown/check-markdown.js#L125-L126
Added lines #L125 - L126 were not covered by tests


[warning] 130-130: scripts/markdown/check-markdown.js#L130
Added line #L130 was not covered by tests


[warning] 134-134: scripts/markdown/check-markdown.js#L134
Added line #L134 was not covered by tests


[warning] 139-139: scripts/markdown/check-markdown.js#L139
Added line #L139 was not covered by tests


[warning] 141-143: scripts/markdown/check-markdown.js#L141-L143
Added lines #L141 - L143 were not covered by tests


[warning] 145-148: scripts/markdown/check-markdown.js#L145-L148
Added lines #L145 - L148 were not covered by tests


[warning] 151-151: scripts/markdown/check-markdown.js#L151
Added line #L151 was not covered by tests


[warning] 157-157: scripts/markdown/check-markdown.js#L157
Added line #L157 was not covered by tests


[warning] 162-162: scripts/markdown/check-markdown.js#L162
Added line #L162 was not covered by tests


[warning] 164-164: scripts/markdown/check-markdown.js#L164
Added line #L164 was not covered by tests


[warning] 166-167: scripts/markdown/check-markdown.js#L166-L167
Added lines #L166 - L167 were not covered by tests

Copy link

@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

🧹 Outside diff range and nitpick comments (3)
scripts/markdown/check-markdown.js (3)

7-11: Consider adding input validation for CONCURRENCY_LIMIT

While the code handles non-integer and negative values, it should also handle:

  • Very large numbers that could cause performance issues
  • Non-numeric strings that would result in NaN
 const parsedLimit = process.env.CONCURRENCY_LIMIT
   ? parseInt(process.env.CONCURRENCY_LIMIT, 10)
   : 10;
 const CONCURRENCY_LIMIT =
-  Number.isInteger(parsedLimit) && parsedLimit > 0 ? parsedLimit : 10;
+  Number.isInteger(parsedLimit) && parsedLimit > 0 && parsedLimit <= 100 ? parsedLimit : 10;
🧰 Tools
🪛 eslint

[error] 7-9: Replace ⏎··?·parseInt(process.env.CONCURRENCY_LIMIT,·10)⏎· with ·?·parseInt(process.env.CONCURRENCY_LIMIT,·10)

(prettier/prettier)


[error] 10-11: Delete ⏎·

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 7-7: scripts/markdown/check-markdown.js#L7
Added line #L7 was not covered by tests


[warning] 11-11: scripts/markdown/check-markdown.js#L11
Added line #L11 was not covered by tests


19-24: Optimize URL validation to avoid object creation

The current implementation creates a new URL object even when validation succeeds. Consider using URL.canParse() for better performance.

 function isValidURL(str) {
-  try {
-    new URL(str);
-    return true;
-  } catch (err) {
-    return false;
-  }
+  return URL.canParse(str);
 }
🧰 Tools
🪛 eslint

[error] 20-20: Do not use 'new' for side effects.

(no-new)

🪛 GitHub Check: codecov/patch

[warning] 19-21: scripts/markdown/check-markdown.js#L19-L21
Added lines #L19 - L21 were not covered by tests


[warning] 23-23: scripts/markdown/check-markdown.js#L23
Added line #L23 was not covered by tests


87-87: Return empty array instead of null for consistency

Both validation functions return null when there are no errors, but returning an empty array would be more consistent and avoid null checks.

-  return errors.length ? errors : null;
+  return errors;

Also applies to: 112-112

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 87-87: scripts/markdown/check-markdown.js#L87
Added line #L87 was not covered by tests

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 86dbaad and 55353d0.

📒 Files selected for processing (1)
  • scripts/markdown/check-markdown.js (4 hunks)
🧰 Additional context used
🪛 eslint
scripts/markdown/check-markdown.js

[error] 7-9: Replace ⏎··?·parseInt(process.env.CONCURRENCY_LIMIT,·10)⏎· with ·?·parseInt(process.env.CONCURRENCY_LIMIT,·10)

(prettier/prettier)


[error] 10-11: Delete ⏎·

(prettier/prettier)


[error] 20-20: Do not use 'new' for side effects.

(no-new)


[error] 34-41: Replace ⏎····'title',⏎····'date',⏎····'type',⏎····'tags',⏎····'cover',⏎····'authors',⏎·· with 'title',·'date',·'type',·'tags',·'cover',·'authors'

(prettier/prettier)


[error] 46-46: Do not access Object.prototype method 'hasOwnProperty' from target object.

(no-prototype-builtins)


[error] 76-78: Replace ⏎············Invalid·URL·for·author·at·index·${index}:·${author.link},⏎·········· with Invalid·URL·for·author·at·index·${index}:·${author.link}

(prettier/prettier)


[error] 105-108: Replace ⏎····frontmatter.weight·===·undefined·||⏎····typeof·frontmatter.weight·!==·'number'⏎·· with frontmatter.weight·===·undefined·||·typeof·frontmatter.weight·!==·'number'

(prettier/prettier)


[error] 121-125: Replace ⏎··folderPath,⏎··validateFunction,⏎··relativePath·=·'',⏎ with folderPath,·validateFunction,·relativePath·=·''

(prettier/prettier)


[error] 144-144: 'err' is already declared in the upper scope on line 128 column 27.

(no-shadow)


[error] 165-165: Delete ,

(prettier/prettier)

🪛 GitHub Check: codecov/patch
scripts/markdown/check-markdown.js

[warning] 4-4: scripts/markdown/check-markdown.js#L4
Added line #L4 was not covered by tests


[warning] 7-7: scripts/markdown/check-markdown.js#L7
Added line #L7 was not covered by tests


[warning] 11-11: scripts/markdown/check-markdown.js#L11
Added line #L11 was not covered by tests


[warning] 19-21: scripts/markdown/check-markdown.js#L19-L21
Added lines #L19 - L21 were not covered by tests


[warning] 23-23: scripts/markdown/check-markdown.js#L23
Added line #L23 was not covered by tests


[warning] 34-34: scripts/markdown/check-markdown.js#L34
Added line #L34 was not covered by tests


[warning] 42-42: scripts/markdown/check-markdown.js#L42
Added line #L42 was not covered by tests


[warning] 45-47: scripts/markdown/check-markdown.js#L45-L47
Added lines #L45 - L47 were not covered by tests


[warning] 52-53: scripts/markdown/check-markdown.js#L52-L53
Added lines #L52 - L53 were not covered by tests


[warning] 57-58: scripts/markdown/check-markdown.js#L57-L58
Added lines #L57 - L58 were not covered by tests


[warning] 62-63: scripts/markdown/check-markdown.js#L62-L63
Added lines #L62 - L63 were not covered by tests


[warning] 67-69: scripts/markdown/check-markdown.js#L67-L69
Added lines #L67 - L69 were not covered by tests


[warning] 71-73: scripts/markdown/check-markdown.js#L71-L73
Added lines #L71 - L73 were not covered by tests


[warning] 75-76: scripts/markdown/check-markdown.js#L75-L76
Added lines #L75 - L76 were not covered by tests


[warning] 80-81: scripts/markdown/check-markdown.js#L80-L81
Added lines #L80 - L81 were not covered by tests


[warning] 87-87: scripts/markdown/check-markdown.js#L87
Added line #L87 was not covered by tests


[warning] 97-97: scripts/markdown/check-markdown.js#L97
Added line #L97 was not covered by tests


[warning] 100-101: scripts/markdown/check-markdown.js#L100-L101
Added lines #L100 - L101 were not covered by tests


[warning] 105-105: scripts/markdown/check-markdown.js#L105
Added line #L105 was not covered by tests


[warning] 109-109: scripts/markdown/check-markdown.js#L109
Added line #L109 was not covered by tests


[warning] 112-112: scripts/markdown/check-markdown.js#L112
Added line #L112 was not covered by tests


[warning] 126-126: scripts/markdown/check-markdown.js#L126
Added line #L126 was not covered by tests


[warning] 128-131: scripts/markdown/check-markdown.js#L128-L131
Added lines #L128 - L131 were not covered by tests


[warning] 134-137: scripts/markdown/check-markdown.js#L134-L137
Added lines #L134 - L137 were not covered by tests


[warning] 140-141: scripts/markdown/check-markdown.js#L140-L141
Added lines #L140 - L141 were not covered by tests


[warning] 144-147: scripts/markdown/check-markdown.js#L144-L147
Added lines #L144 - L147 were not covered by tests


[warning] 151-155: scripts/markdown/check-markdown.js#L151-L155
Added lines #L151 - L155 were not covered by tests


[warning] 157-161: scripts/markdown/check-markdown.js#L157-L161
Added lines #L157 - L161 were not covered by tests

🪛 Biome (1.9.4)
scripts/markdown/check-markdown.js

[error] 46-46: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🔇 Additional comments (3)
scripts/markdown/check-markdown.js (3)

4-4: Add 'p-limit' to the project's dependencies

The p-limit library is being used but is not listed in the project's dependencies.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 4-4: scripts/markdown/check-markdown.js#L4
Added line #L4 was not covered by tests


46-47: ⚠️ Potential issue

Use Object.hasOwn() instead of hasOwnProperty

Direct use of hasOwnProperty is discouraged as it can be overridden.

-    if (!frontmatter.hasOwnProperty(attr)) {
+    if (!Object.hasOwn(frontmatter, attr)) {

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 46-46: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🪛 eslint

[error] 46-46: Do not access Object.prototype method 'hasOwnProperty' from target object.

(no-prototype-builtins)

🪛 GitHub Check: codecov/patch

[warning] 45-47: scripts/markdown/check-markdown.js#L45-L47
Added lines #L45 - L47 were not covered by tests


Line range hint 1-167: Add test coverage for the validation functionality

The code lacks test coverage as indicated by the codecov report. Consider adding tests for:

  • URL validation
  • Blog frontmatter validation
  • Docs frontmatter validation
  • File processing with concurrency
🧰 Tools
🪛 Biome (1.9.4)

[error] 46-46: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🪛 eslint

[error] 34-41: Replace ⏎····'title',⏎····'date',⏎····'type',⏎····'tags',⏎····'cover',⏎····'authors',⏎·· with 'title',·'date',·'type',·'tags',·'cover',·'authors'

(prettier/prettier)


[error] 46-46: Do not access Object.prototype method 'hasOwnProperty' from target object.

(no-prototype-builtins)


[error] 76-78: Replace ⏎············Invalid·URL·for·author·at·index·${index}:·${author.link},⏎·········· with Invalid·URL·for·author·at·index·${index}:·${author.link}

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 34-34: scripts/markdown/check-markdown.js#L34
Added line #L34 was not covered by tests


[warning] 42-42: scripts/markdown/check-markdown.js#L42
Added line #L42 was not covered by tests


[warning] 45-47: scripts/markdown/check-markdown.js#L45-L47
Added lines #L45 - L47 were not covered by tests


[warning] 52-53: scripts/markdown/check-markdown.js#L52-L53
Added lines #L52 - L53 were not covered by tests


[warning] 57-58: scripts/markdown/check-markdown.js#L57-L58
Added lines #L57 - L58 were not covered by tests


[warning] 62-63: scripts/markdown/check-markdown.js#L62-L63
Added lines #L62 - L63 were not covered by tests


[warning] 67-69: scripts/markdown/check-markdown.js#L67-L69
Added lines #L67 - L69 were not covered by tests


[warning] 71-73: scripts/markdown/check-markdown.js#L71-L73
Added lines #L71 - L73 were not covered by tests


[warning] 75-76: scripts/markdown/check-markdown.js#L75-L76
Added lines #L75 - L76 were not covered by tests


[warning] 80-81: scripts/markdown/check-markdown.js#L80-L81
Added lines #L80 - L81 were not covered by tests


[warning] 87-87: scripts/markdown/check-markdown.js#L87
Added line #L87 was not covered by tests

Comment on lines +128 to +167
fs.readdir(folderPath, (err, files) => {
if (err) {
console.error('Error reading directory:', err);
return;
}

files.forEach(file => {
const filePath = path.join(folderPath, file);
const relativeFilePath = path.join(relativePath, file);
files.map((file) =>
limit(() => {
const filePath = path.join(folderPath, file);
const relativeFilePath = path.join(relativePath, file);

// Skip the folder 'docs/reference/specification'
if (relativeFilePath.includes('reference/specification')) {
return;
}
// Skip auto-generated specification documentation
if (relativeFilePath.includes('reference/specification')) {
return;
}

fs.stat(filePath, (err, stats) => {
if (err) {
console.error('Error reading file stats:', err);
return;
}

// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);

const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach(error => console.log(` - ${error}`));
process.exitCode = 1;
}
}
});
fs.stat(filePath, (err, stats) => {
if (err) {
console.error('Error reading file stats:', err);
return;
}

// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);

const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach((error) => console.log(` - ${error}`));
process.exitCode = 1;
}
}
});
});
}),
);
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use Promise-based API for consistent async operations

The current implementation mixes callbacks and async/await. Consider using the Promise-based fs API consistently.

-  fs.readdir(folderPath, (err, files) => {
-    if (err) {
-      console.error('Error reading directory:', err);
-      return;
-    }
+  try {
+    const files = await fs.promises.readdir(folderPath);
+    await Promise.all(
+      files.map((file) =>
+        limit(async () => {
           const filePath = path.join(folderPath, file);
           const relativeFilePath = path.join(relativePath, file);

           if (relativeFilePath.includes('reference/specification')) {
             return;
           }

-          fs.stat(filePath, (err, stats) => {
-            if (err) {
-              console.error('Error reading file stats:', err);
-              return;
-            }
+          try {
+            const stats = await fs.promises.stat(filePath);

             if (stats.isDirectory()) {
-              checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
+              await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
             } else if (path.extname(file) === '.md') {
-              const fileContent = fs.readFileSync(filePath, 'utf-8');
+              const fileContent = await fs.promises.readFile(filePath, 'utf-8');
               const { data: frontmatter } = matter(fileContent);

               const errors = validateFunction(frontmatter);
               if (errors) {
                 console.log(`Errors in file ${relativeFilePath}:`);
                 errors.forEach((error) => console.log(` - ${error}`));
                 process.exitCode = 1;
               }
             }
-          });
+          } catch (error) {
+            console.error(`Error processing file ${relativeFilePath}:`, error);
+          }
+        })
+      )
+    );
+  } catch (error) {
+    console.error('Error reading directory:', error);
+  }
-  });
📝 Committable suggestion

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

Suggested change
fs.readdir(folderPath, (err, files) => {
if (err) {
console.error('Error reading directory:', err);
return;
}
files.forEach(file => {
const filePath = path.join(folderPath, file);
const relativeFilePath = path.join(relativePath, file);
files.map((file) =>
limit(() => {
const filePath = path.join(folderPath, file);
const relativeFilePath = path.join(relativePath, file);
// Skip the folder 'docs/reference/specification'
if (relativeFilePath.includes('reference/specification')) {
return;
}
// Skip auto-generated specification documentation
if (relativeFilePath.includes('reference/specification')) {
return;
}
fs.stat(filePath, (err, stats) => {
if (err) {
console.error('Error reading file stats:', err);
return;
}
// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);
const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach(error => console.log(` - ${error}`));
process.exitCode = 1;
}
}
});
fs.stat(filePath, (err, stats) => {
if (err) {
console.error('Error reading file stats:', err);
return;
}
// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);
const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach((error) => console.log(` - ${error}`));
process.exitCode = 1;
}
}
});
});
}),
);
});
try {
const files = await fs.promises.readdir(folderPath);
await Promise.all(
files.map((file) =>
limit(async () => {
const filePath = path.join(folderPath, file);
const relativeFilePath = path.join(relativePath, file);
if (relativeFilePath.includes('reference/specification')) {
return;
}
try {
const stats = await fs.promises.stat(filePath);
if (stats.isDirectory()) {
await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = await fs.promises.readFile(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);
const errors = validateFunction(frontmatter);
if (errors) {
console.log(`Errors in file ${relativeFilePath}:`);
errors.forEach((error) => console.log(` - ${error}`));
process.exitCode = 1;
}
}
} catch (error) {
console.error(`Error processing file ${relativeFilePath}:`, error);
}
})
)
);
} catch (error) {
console.error('Error reading directory:', error);
}
🧰 Tools
🪛 eslint

[error] 144-144: 'err' is already declared in the upper scope on line 128 column 27.

(no-shadow)


[error] 165-165: Delete ,

(prettier/prettier)

🪛 GitHub Check: codecov/patch

[warning] 128-131: scripts/markdown/check-markdown.js#L128-L131
Added lines #L128 - L131 were not covered by tests


[warning] 134-137: scripts/markdown/check-markdown.js#L134-L137
Added lines #L134 - L137 were not covered by tests


[warning] 140-141: scripts/markdown/check-markdown.js#L140-L141
Added lines #L140 - L141 were not covered by tests


[warning] 144-147: scripts/markdown/check-markdown.js#L144-L147
Added lines #L144 - L147 were not covered by tests


[warning] 151-155: scripts/markdown/check-markdown.js#L151-L155
Added lines #L151 - L155 were not covered by tests


[warning] 157-161: scripts/markdown/check-markdown.js#L157-L161
Added lines #L157 - L161 were not covered by tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Limit concurrency in markdown file processing to improve performance
3 participants