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

refactor: migrate build script to typescript #3423

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

Conversation

JeelRajodiya
Copy link
Contributor

@JeelRajodiya JeelRajodiya commented Nov 22, 2024

Description

  • The PR migrates the build scripts to Typescript

Related issue(s)
Related to #3187

Summary by CodeRabbit

  • New Features

    • Introduced new TypeScript configurations and interfaces for improved type safety and organization.
    • Added functionality for internationalization with the new configuration file for next-i18next.
    • Implemented several new scripts for processing and validating markdown files, generating RSS feeds, and building structured lists of posts and tools.
    • Added new utility functions for reading and writing JSON files, and converting content from YAML or JSON formats.
    • Enhanced functionality for GitHub data extraction and tool combination processes.
    • Updated scripts to include enhanced navigation and document processing logic.
    • Introduced new scripts for handling case studies and finance data processing.
  • Bug Fixes

    • Enhanced error handling across various scripts to ensure robust processing and validation.
  • Refactor

    • Transitioned multiple scripts from CommonJS to ES module syntax.
    • Updated import paths and improved code formatting for consistency.
  • Chores

    • Removed outdated or redundant scripts and files to streamline the codebase.

Copy link

coderabbitai bot commented Nov 22, 2024

Warning

Rate limit exceeded

@JeelRajodiya has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 46 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9059384 and 7007d2f.

📒 Files selected for processing (1)
  • scripts/markdown/check-markdown.ts (1 hunks)

Walkthrough

This pull request introduces modifications to various configuration and script files, including updates to ESLint settings, GitHub Actions workflows, and the addition of TypeScript configurations. Key changes include the introduction of a new next-i18next.config.cjs file for internationalization, the transition of several scripts from JavaScript to TypeScript, and the enhancement of type safety across the project. The workflow for Node.js pull request testing has also been refined to conditionally install dependencies based on the presence of a package.json file.

Changes

File/Directory Change Summary
.eslintrc Added override for scripts/**/* directory, disabled specific ESLint rules
.github/workflows/if-nodejs-pr-testing.yml Modified dependency installation step to use npm install based on the presence of package.json
next-i18next.config.cjs New configuration file for internationalization settings
next-i18next.config.js Removed in favor of .cjs version
package.json Added "type": "module", updated script commands to use tsx, added TypeScript type dependencies
scripts/ Comprehensive migration of utility scripts from JavaScript to TypeScript
types/ Added multiple TypeScript type definitions for various modules and scripts
tsconfig.json Updated configuration, added ts-node settings

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant Repo as Repository
    participant CI as CI/CD Pipeline
    participant Build as Build Process

    Dev->>Repo: Commits changes
    Repo->>CI: Triggers workflow
    CI->>Build: Runs TypeScript compilation
    Build->>Build: Validates scripts
    Build->>Build: Converts to ES modules
    Build-->>CI: Returns build status
    CI-->>Dev: Provides feedback
Loading

Poem

🐰 Hop, skip, and a TypeScript jump!
Modules dancing, no more CommonJS slump
Scripts transformed with type-safe grace
ES modules now set the pace
A rabbit's code, clean and bright 🌟


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. (Beta)
  • @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.

Comment on lines +319 to +322
"import/no-extraneous-dependencies": "off",
"no-console": "off",
"require-jsdoc":"off"
}
Copy link
Member

Choose a reason for hiding this comment

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

Why you added these three rules for scripts folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

import/no-extraneous-dependencies: The Scripts are using some of the dependencies which are listed in DevDependencies in package.json file. Since the scripts are ran in the server, it does not matter where the dependencies are listed in package.json file.

This error was occuring in the scripts/compose.ts file

image

no-console: We have several console.log statements in the scripts, which are necessary for build scripts. So I have removed the eslint warning since it is evident for the build scripts to have console statements in them (view your old comment)

require-jsdoc: We are migrating the files to typescript, I have removed the jsdoc rule because it would be irrelevant to keep jsdoc in typescript files.


import { writeJSON } from '../utils/readAndWriteJson';

const currentFilePath = fileURLToPath(import.meta.url);
Copy link
Member

Choose a reason for hiding this comment

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

What is import.meta.url here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is an alternative way of getting the current currentFilePath and currentDirPath. Apprently __dirname and and __filename are not in available in es modules.

Value of import.meta.url in /scripts/index.ts

image

Reference: Alternative for __dirname in Node.js when using ES6 modules

async function pause(ms) {
import { graphql } from '@octokit/graphql';
import assert from 'assert';
import { writeFile } from 'fs-extra';
Copy link
Member

Choose a reason for hiding this comment

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

Add the types of this library as devDependency in package.json

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have searched online but couldn't find types for @octokit/graphql library. So I have declared necessary types manually in the /types directory.

Other libraries such as assert, fs-extra takes type definitions from @types/node which We have already installed.

@akshatnema
Copy link
Member

@coderabbitai review

Copy link

coderabbitai bot commented Dec 21, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 25

🔭 Outside diff range comments (2)
scripts/dashboard/build-dashboard.ts (1)

Line range hint 96-137: Guard against nullish “discussion.reactions” or “discussion.comments”
Although the code uses non-null assertions, there could be edge cases where GitHub returns unexpected structures (for example, missing “comments” or “reactions”). It may be safer to use optional chaining or explicit checks before reducing or accessing nested properties.

Below is a sample diff to handle these edge cases more gracefully:

- const interactionsCount =
-   discussion.reactions.totalCount +
-   discussion.comments.totalCount +
-   discussion.comments.nodes.reduce((acc, curr) => acc + curr.reactions.totalCount, 0);

+ const totalReactions = discussion.reactions?.totalCount ?? 0;
+ const totalComments = discussion.comments?.totalCount ?? 0;
+ const nestedReactions = discussion.comments?.nodes?.reduce((acc, curr) => acc + (curr.reactions?.totalCount ?? 0), 0) ?? 0;
+ const interactionsCount = totalReactions + totalComments + nestedReactions;
scripts/index.ts (1)

Line range hint 14-47: Add return type and improve error handling for async operations

The start function needs better error handling and type annotations.

-async function start() {
+async function start(): Promise<void> {
+  try {
     await buildPostList();
     rssFeed('blog', 'AsyncAPI Initiative Blog RSS Feed', 'AsyncAPI Initiative Blog', 'rss.xml');
     await buildCaseStudiesList('config/casestudies', resolve(currentDirPath, '../config', 'case-studies.json'));
     await buildAdoptersList();
     
     // ... rest of the function ...
+  } catch (error: unknown) {
+    const errorMessage = error instanceof Error ? error.message : String(error);
+    console.error(`Build process failed: ${errorMessage}`);
+    process.exit(1);
+  }
 }
🧹 Nitpick comments (46)
scripts/build-rss.ts (4)

7-8: Avoid direct dynamic import of large data if alternatives exist.
If the JSON file is large, consider an approach that can lazily load or partially load data to improve performance and memory usage.


26-28: Avoid using "as any[]" for typed collections.
Explicitly define the type of the posts array (e.g., an interface) to ensure type safety.


46-48: Consider making base URL configurable.
It might be preferable to load the domain from a config file or environment variable for different deployment environments.


93-105: Use the actual image size for enclosure length.
The code currently uses a dummy value for @length. If accuracy is important, consider retrieving the actual content length of the image.

scripts/tools/tools-object.ts (3)

7-12: Consider caching repeated fetches.
Repeatedly fetching large files over the network can degrade performance. If feasible, cache results for multiple usages in a single run.


33-54: Return early if required fields are missing.
In createToolObject, you rely on optional chaining for fields like description or links.repoUrl. If these are truly required, consider more direct validation or fallback defaults for better clarity.


75-90: Enforce stricter concurrency or parallelism boundaries.
Promises are run in parallel, which is good for performance, but if there's a large number of tool files, it may stress resources. Use concurrency controls if needed.

scripts/markdown/check-markdown.ts (4)

31-74: Consider supporting custom link checking or advanced metadata validation.
While validating format is good, you may want to scan for broken links or missing alt text for images to further enhance content quality.


76-77: Add optional attribute checks for some frontmatter fields.
Though the blog post authors field is checked, consider a deep check to ensure consistent data structure (like an author object must always have non-empty strings for keys).


113-116: Explicitly log a skip message for “docs/reference/specification”.
When skipping a directory, consider adding a log statement so users or maintainers can quickly see what was skipped and why.


146-155: Wrap bulk operations with additional error details.
When multiple markdown files fail, it may be helpful to summarize the total errors at the end for a single glance rather than scanning many lines in logs.

scripts/tools/combine-tools.ts (1)

96-114: Evaluate memory usage with repeated updates to Fuse instances.
Re-instantiating Fuse after adding new items is convenient but can be expensive if large. Consider a single pass to add items, then one final initialization, if scaling is expected.

scripts/dashboard/build-dashboard.ts (2)

Line range hint 39-78: Clarify the return type and pagination logic
The current logic assumes a result shape that includes “search.nodes” and “search.pageInfo”. Consider creating a dedicated type to annotate the GraphQL response, improving clarity around the structure, especially since the function recurses for pagination. This will make it easier for contributors to understand and maintain the code.


217-227: Exported entities confirmation
This file exports many functions. Ensure that only the functions needed by external modules are part of the public API, to keep the code surface minimal and maintainable.

scripts/build-post-list.ts (2)

37-52: Consider simplifying slug creation
The function slugifyToC uses regex matching for inline anchor tags and fallback slug generation. You could unify both conditions in a single regex or apply a more streamlined approach for better readability.


178-188: Handle potential I/O errors
The script writes the final JSON to disk in production mode. It’s worth adding a confirmation log or a try/catch to handle unexpected file system errors, ensuring any failure is reported properly and does not silently halt the build process.

types/packages/jgexml__json2xml.d.ts (1)

2-3: Provide more usage context or doc comments
Although this declaration provides the essential contract for getXml, consider adding a short JSDoc comment describing “feed” and other parameters, guiding users on what data structure “feed” expects.

scripts/adopters/index.ts (1)

9-11: Verify async operation success
The writeJSON function is called but not awaited. Although it might happen nearly instantaneously, consider awaiting it or handling potential errors to ensure the file write finishes before proceeding.

next-i18next.config.cjs (1)

11-11: Improve documentation for useSuspense setting

The comment "this line" doesn't explain why useSuspense is disabled.

-    react: { useSuspense: false } // this line
+    react: { useSuspense: false } // Disable React suspense to prevent hydration issues with SSR
scripts/tools/extract-tools-github.ts (1)

9-14: Handle GitHub API rate limits and pagination

The GitHub Search API has rate limits and returns paginated results. Add proper handling:

+const GITHUB_API_URL = 'https://api.github.com/search/code';
+const SEARCH_QUERY = 'filename:.asyncapi-tool';
+
+async function checkRateLimit() {
+  const response = await axios.get('https://api.github.com/rate_limit', {
+    headers: {
+      authorization: `token ${process.env.GITHUB_TOKEN}`
+    }
+  });
+  const { remaining, reset } = response.data.rate.search;
+  if (remaining === 0) {
+    const resetDate = new Date(reset * 1000);
+    throw new Error(`GitHub API rate limit exceeded. Resets at ${resetDate.toISOString()}`);
+  }
+}
+
 const getData = async (): Promise<GitHubSearchResponse> => {
+  await checkRateLimit();
+
   const result = await axios.get(
-    'https://api.github.com/search/code?q=filename:.asyncapi-tool',
+    `${GITHUB_API_URL}?q=${SEARCH_QUERY}&per_page=100`,
     {
       headers: {
         accept: 'application/vnd.github.text-match+json',
tsconfig.json (1)

21-24: Consider performance implications of transpileOnly

The ts-node configuration with transpileOnly: true will improve development speed but skips type checking. Consider adding a pre-commit hook or CI check to ensure type checking is performed.

types/scripts/build-posts-list.ts (1)

26-26: Consider replacing index signature with specific properties

Using [key: string]: any reduces type safety. If possible, explicitly define all expected properties.

scripts/utils/readAndWriteJson.ts (1)

5-29: Consider enhancing error handling and type safety

While the error handling structure is good, consider these improvements:

  1. Use more specific error types
  2. Add input validation
  3. Type the JSON content

Here's a suggested implementation:

-export async function writeJSON(readPath: string, writePath: string) {
+export async function writeJSON(readPath: string, writePath: string): Promise<void> {
+  if (!readPath || !writePath) {
+    throw new Error('Both readPath and writePath must be provided');
+  }
+
   let readContent: string;
-  let jsonContent;
+  let jsonContent: Record<string, unknown>;

   // Attempt to read the file
   try {
     readContent = await readFile(readPath, 'utf-8');
   } catch (err) {
-    throw new Error(`Error while reading file\nError: ${err}`);
+    throw new Error(`Failed to read file ${readPath}: ${err instanceof Error ? err.message : String(err)}`);
   }

   // Attempt to convert content to JSON
   try {
     jsonContent = convertToJson(readContent);
   } catch (err) {
-    throw new Error(`Error while conversion\nError: ${err}`);
+    throw new Error(`Failed to parse JSON from ${readPath}: ${err instanceof Error ? err.message : String(err)}`);
   }

   // Attempt to write the JSON content to file
   try {
-    await writeFile(writePath, JSON.stringify(jsonContent));
+    await writeFile(writePath, JSON.stringify(jsonContent, null, 2));
   } catch (err) {
-    throw new Error(`Error while writing file\nError: ${err}`);
+    throw new Error(`Failed to write to ${writePath}: ${err instanceof Error ? err.message : String(err)}`);
   }
 }
scripts/casestudies/index.ts (1)

5-26: Enhance type safety and error handling

Consider adding return type annotation and more specific error handling.

Here's a suggested implementation:

-export async function buildCaseStudiesList(dirWithCaseStudy: string, writeFilePath: string) {
+type CaseStudy = Record<string, unknown>;
+export async function buildCaseStudiesList(
+  dirWithCaseStudy: string,
+  writeFilePath: string
+): Promise<CaseStudy[]> {
   try {
     const files = await readdir(dirWithCaseStudy);

     // Process all files in parallel using Promise.all
     const caseStudiesList = await Promise.all(
       files.map(async (file) => {
         const caseStudyFileName = join(dirWithCaseStudy, file);
         const caseStudyContent = await readFile(caseStudyFileName, 'utf-8');

         return convertToJson(caseStudyContent) as CaseStudy;
       })
     );

     // Write the complete list once after all files are processed
     await writeFile(writeFilePath, JSON.stringify(caseStudiesList, null, 2));

     return caseStudiesList;
   } catch (err) {
-    throw new Error(err instanceof Error ? err.message : String(err));
+    throw new Error(
+      `Failed to build case studies list: ${err instanceof Error ? err.message : String(err)}`
+    );
   }
 }
scripts/utils.ts (1)

15-25: Enhance error handling with typed errors

The error handling could be improved by using typed errors and providing more context.

-  } catch (jsonError) {
+  } catch (jsonError: unknown) {
     try {
       const yamlContent = yaml.parse(contentYAMLorJSON);
       return yamlContent;
-    } catch (yamlError) {
+    } catch (yamlError: unknown) {
+      const jsonErr = jsonError instanceof Error ? jsonError.message : String(jsonError);
+      const yamlErr = yamlError instanceof Error ? yamlError.message : String(yamlError);
       throw new Error(
-        `Invalid content format:\nJSON Parse Error: ${jsonError}\nYAML Parse Error: ${yamlError}`
+        `Invalid content format:\nJSON Parse Error: ${jsonErr}\nYAML Parse Error: ${yamlErr}`
       );
     }
   }
scripts/finance/index.ts (2)

23-34: Add file existence checks before operations

Add checks to verify if the source YAML files exist before attempting to process them.

+import { access } from 'fs/promises';
+
 try {
   const expensesPath = resolve(currentDir, configDir, financeDir, year, 'Expenses.yml');
   const expensesLinkPath = resolve(currentDir, configDir, financeDir, year, 'ExpensesLink.yml');

+  // Check if source files exist
+  try {
+    await access(expensesPath);
+    await access(expensesLinkPath);
+  } catch {
+    throw new Error(`Required files not found: ${expensesPath} or ${expensesLinkPath}`);
+  }

   // Ensure the directory exists before writing the files

39-42: Improve error handling with specific error types

The error handling could be more specific and provide better context.

-  } catch (err) {
-    assert(err instanceof Error);
-    throw new Error(err.message);
+  } catch (err: unknown) {
+    const errorMessage = err instanceof Error ? err.message : String(err);
+    throw new Error(`Failed to build finance info list: ${errorMessage}`);
   }
scripts/index.ts (2)

11-12: Consider using a configuration file for paths

The current directory resolution could be moved to a configuration file for better maintainability.

Consider creating a config.ts file to store all path configurations.


Line range hint 48-51: Consider removing immediate invocation

The immediate invocation of start() at the module level could cause issues with testing and module imports.

Consider moving the start() invocation to a separate file or adding a condition:

 export { start };
 
-start();
+if (require.main === module) {
+  start().catch((error) => {
+    console.error('Failed to start build process:', error);
+    process.exit(1);
+  });
+}
scripts/build-tools.ts (1)

13-26: Consider adding input validation and return type annotation

The buildTools function should validate input parameters and specify its return type for better type safety.

-const buildTools = async (automatedToolsPath: string, manualToolsPath: string, toolsPath: string, tagsPath: string) => {
+const buildTools = async (automatedToolsPath: string, manualToolsPath: string, toolsPath: string, tagsPath: string): Promise<void> => {
+  // Validate input paths
+  if (!automatedToolsPath || !manualToolsPath || !toolsPath || !tagsPath) {
+    throw new Error('All path parameters are required');
+  }
   try {
scripts/build-pages.ts (2)

1-2: Consider using fs-extra for better error handling

Replace fs with fs-extra for Promise-based APIs and better error handling.

-import fs from 'fs';
+import fs from 'fs-extra';

24-24: Add return type annotation and error handling

The function should specify its return type and include proper error handling.

-export function copyAndRenameFiles(srcDir: string, targetDir: string) {
+export function copyAndRenameFiles(srcDir: string, targetDir: string): void {
+  if (!fs.existsSync(srcDir)) {
+    throw new Error(`Source directory ${srcDir} does not exist`);
+  }
types/githubGraphQL.ts (1)

91-95: Add JSDoc documentation and fix EOF newline

Add documentation for the generic type parameter and fix the missing newline at EOF.

+/**
+ * Represents a GraphQL query response
+ * @template T The type of the node in the response
+ */
 export interface QueryResponse<T> {
   node?: T;
   rateLimit: RateLimit;
   search?: SearchResult;
-}
\ No newline at end of file
+}
+
🧰 Tools
🪛 eslint

[error] 95-95: Newline required at end of file but not found.

(eol-last)


[error] 95-95: Insert

(prettier/prettier)

types/scripts/tools.ts (2)

39-44: Consider making categories property more type-safe

The categories array allows both Category and string types, which could lead to runtime errors if arbitrary strings are used. Consider restricting it to just the Category type.

interface Filters {
  language?: Array<LanguageColorItem | string>;
  technology?: Array<LanguageColorItem | string>;
-  categories: Array<Category | string>;
+  categories: Array<Category>;
  hasCommercial?: boolean;
}

54-69: Consider using Pick/Omit utility types for repository structure

The nested structure in ToolsData could benefit from TypeScript's utility types to make it more maintainable.

+ type Repository = {
+   full_name: string;
+   html_url: string;
+   owner: {
+     login: string;
+   };
+   description: string;
+ };

export type ToolsData = {
  items: {
    name: string;
    url: string;
    path: string;
    html_url: string;
-   repository: {
-     full_name: string;
-     html_url: string;
-     owner: {
-       login: string;
-     };
-     description: string;
-   };
+   repository: Repository;
  }[];
};
scripts/build-newsroom-videos.ts (1)

19-26: Consider extracting API parameters to configuration

The YouTube API parameters are hardcoded. Consider moving them to a configuration file for better maintainability.

// config/youtube.ts
export const YOUTUBE_CONFIG = {
  channelId: 'UCIz9zGwDLbrYQcDKVXdOstQ',
  maxResults: 5,
  // ... other parameters
} as const;
scripts/build-meetings.ts (2)

27-28: Extract date calculations to named constants

The date calculations are hard to understand at first glance. Consider extracting them to named constants.

- const timeMin = new Date(Date.parse(currentTime) - 100 * 24 * 60 * 60 * 1000).toISOString();
- const timeMax = new Date(Date.parse(currentTime) + 30 * 24 * 60 * 60 * 1000).toISOString();
+ const DAYS_TO_LOOK_BACK = 100;
+ const DAYS_TO_LOOK_AHEAD = 30;
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
+ const timeMin = new Date(Date.parse(currentTime) - DAYS_TO_LOOK_BACK * MS_PER_DAY).toISOString();
+ const timeMax = new Date(Date.parse(currentTime) + DAYS_TO_LOOK_AHEAD * MS_PER_DAY).toISOString();

48-53: Use optional chaining as suggested by static analysis

The static analysis tool correctly identifies a potential improvement using optional chaining.

- e.extendedProperties?.private &&
-   `https://github.com/asyncapi/community/issues/${e.extendedProperties.private.ISSUE_ID}`,
- banner: e.extendedProperties?.private && e.extendedProperties.private.BANNER,
+ url: e.extendedProperties?.private?.ISSUE_ID &&
+   `https://github.com/asyncapi/community/issues/${e.extendedProperties.private.ISSUE_ID}`,
+ banner: e.extendedProperties?.private?.BANNER,
🧰 Tools
🪛 Biome (1.9.4)

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

types/scripts/dashboard.ts (1)

22-28: Consider extracting common node properties

The PullRequestById.node and IssueById.node share many properties with other interfaces. Consider creating a base interface for common node properties.

+interface BaseNode {
+  __typename: string;
+  assignees: Assignees;
+  timelineItems: TimelineItems;
+  author: Author;
+  id: string;
+  title: string;
+  resourcePath: string;
+  repository: Repository;
+  reactions: Reactions;
+  comments: Comments;
+  labels: {
+    nodes: Label[];
+  };
+}

-export interface PullRequestById {
-  node: {
-    // ... current properties
-  };
-}
+export interface PullRequestById {
+  node: BaseNode & {
+    reviews: Reviews;
+  };
+}

-export interface IssueById {
-  node: {
-    // ... current properties
-  };
+export interface IssueById {
+  node: BaseNode;
+}

Also applies to: 56-73, 75-92

scripts/tools/tags-color.ts (1)

149-177: Consider unique color schemes for technologies

Multiple technologies (Kubernetes-native, Jenkins, Flask) share the same color scheme (#D7C7F2/#A387D2). Consider assigning unique colors to each technology for better visual distinction.

   {
     name: 'Jenkins',
-    color: 'bg-[#D7C7F2]',
-    borderColor: 'border-[#A387D2]'
+    color: 'bg-[#335061]',
+    borderColor: 'border-[#224759]'
   },
   {
     name: 'Flask',
-    color: 'bg-[#D7C7F2]',
-    borderColor: 'border-[#A387D2]'
+    color: 'bg-[#000000]',
+    borderColor: 'border-[#424242]'
   },
scripts/dashboard/issue-queries.ts (1)

Line range hint 1-277: Consider adding TypeScript types for GraphQL queries

Since we're migrating to TypeScript, consider adding type definitions for the GraphQL queries' responses to improve type safety and developer experience.

type PullRequestResponse = {
  node: {
    __typename: string;
    assignees: {
      totalCount: number;
    };
    // ... other fields
  };
  rateLimit: {
    limit: number;
    cost: number;
    remaining: number;
    resetAt: string;
  };
};

export const Queries = Object.freeze({
  pullRequestById: `...` as const,
  // ... other queries
});
scripts/compose.ts (2)

144-150: Consider using async/await for file operations

The callback-style file operations could be modernized using async/await for better error handling and readability.

const writePost = async (filePath: string, frontMatter: string) => {
  try {
    await fs.promises.writeFile(filePath, frontMatter, { flag: 'wx' });
    console.log(`Blog post generated successfully at ${filePath}`);
  } catch (err) {
    if (err.code === 'EEXIST') {
      throw new Error(`File ${filePath} already exists`);
    }
    throw err;
  }
};

153-159: Enhance error messages with more context

The error messages could be more descriptive to help users understand and resolve issues.

.catch((error) => {
  console.error('Failed to generate blog post:', error);
  if (error.isTtyError) {
    console.log("The prompt couldn't be rendered in your current terminal environment");
  } else {
    console.log('An unexpected error occurred while generating the blog post. Please check your inputs and try again.');
  }
});
scripts/build-docs.ts (1)

138-138: Use array destructuring for clarity

Replace array filtering with destructuring for better readability.

-structuredPosts[0] = docPosts.filter((p) => p.slug === '/docs')[0];
+const [welcomePost] = docPosts.filter((p) => p.slug === '/docs');
+structuredPosts[0] = welcomePost;
🧰 Tools
🪛 eslint

[error] 138-138: Use array destructuring.

(prefer-destructuring)

scripts/tools/tools-schema.json (1)

13-16: Fix grammatical errors in description

The description property contains grammatical errors that should be corrected for clarity.

-      "description": "By default scripts read description of repository there project is stored. You can override this behaviour by providing custom description."
+      "description": "By default, scripts read the description from the repository where the project is stored. You can override this behavior by providing a custom description."
utils/getStatic.ts (1)

31-37: Consider improving TypeScript types

The any type for ctx parameter could be replaced with a more specific type to improve type safety during the TypeScript migration.

Consider defining an interface for the context parameter:

interface I18nContext {
  params?: {
    lang?: string;
  };
}

Then update the function signature:

- export async function getI18nProps(ctx: any, ns = ['common']) {
+ export async function getI18nProps(ctx: I18nContext, ns = ['common']) {
🧰 Tools
🪛 eslint

[error] 34-34: Delete ,

(prettier/prettier)


[error] 34-34: 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 9c1fda0 and 4bf9c1b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (55)
  • .eslintrc (1 hunks)
  • .github/workflows/if-nodejs-pr-testing.yml (1 hunks)
  • next-i18next.config.cjs (1 hunks)
  • next-i18next.config.js (0 hunks)
  • package.json (3 hunks)
  • pages/_document.tsx (1 hunks)
  • rename.ps1 (1 hunks)
  • scripts/adopters/index.js (0 hunks)
  • scripts/adopters/index.ts (1 hunks)
  • scripts/build-docs.ts (7 hunks)
  • scripts/build-meetings.ts (2 hunks)
  • scripts/build-newsroom-videos.js (0 hunks)
  • scripts/build-newsroom-videos.ts (1 hunks)
  • scripts/build-pages.ts (2 hunks)
  • scripts/build-post-list.js (0 hunks)
  • scripts/build-post-list.ts (1 hunks)
  • scripts/build-rss.js (0 hunks)
  • scripts/build-rss.ts (1 hunks)
  • scripts/build-tools.js (0 hunks)
  • scripts/build-tools.ts (1 hunks)
  • scripts/casestudies/index.js (0 hunks)
  • scripts/casestudies/index.ts (1 hunks)
  • scripts/compose.ts (2 hunks)
  • scripts/dashboard/build-dashboard.ts (9 hunks)
  • scripts/dashboard/issue-queries.ts (2 hunks)
  • scripts/finance/index.js (0 hunks)
  • scripts/finance/index.ts (1 hunks)
  • scripts/index.ts (2 hunks)
  • scripts/markdown/check-markdown.js (0 hunks)
  • scripts/markdown/check-markdown.ts (1 hunks)
  • scripts/tools/categorylist.js (0 hunks)
  • scripts/tools/categorylist.ts (1 hunks)
  • scripts/tools/combine-tools.js (0 hunks)
  • scripts/tools/combine-tools.ts (1 hunks)
  • scripts/tools/extract-tools-github.js (0 hunks)
  • scripts/tools/extract-tools-github.ts (1 hunks)
  • scripts/tools/tags-color.js (0 hunks)
  • scripts/tools/tags-color.ts (1 hunks)
  • scripts/tools/tools-object.js (0 hunks)
  • scripts/tools/tools-object.ts (1 hunks)
  • scripts/tools/tools-schema.json (1 hunks)
  • scripts/utils.js (0 hunks)
  • scripts/utils.ts (1 hunks)
  • scripts/utils/readAndWriteJson.js (0 hunks)
  • scripts/utils/readAndWriteJson.ts (1 hunks)
  • tsconfig.json (1 hunks)
  • types/githubGraphQL.ts (1 hunks)
  • types/packages/jgexml__json2xml.d.ts (1 hunks)
  • types/packages/markdown-toc.d.ts (1 hunks)
  • types/scripts/build-posts-list.ts (1 hunks)
  • types/scripts/build-rss.ts (1 hunks)
  • types/scripts/dashboard.ts (1 hunks)
  • types/scripts/tools.ts (1 hunks)
  • utils/getStatic.ts (5 hunks)
  • utils/languageDetector.ts (1 hunks)
💤 Files with no reviewable changes (16)
  • scripts/tools/extract-tools-github.js
  • scripts/tools/categorylist.js
  • next-i18next.config.js
  • scripts/utils.js
  • scripts/utils/readAndWriteJson.js
  • scripts/adopters/index.js
  • scripts/finance/index.js
  • scripts/tools/tools-object.js
  • scripts/casestudies/index.js
  • scripts/build-tools.js
  • scripts/build-rss.js
  • scripts/tools/tags-color.js
  • scripts/tools/combine-tools.js
  • scripts/build-post-list.js
  • scripts/build-newsroom-videos.js
  • scripts/markdown/check-markdown.js
✅ Files skipped from review due to trivial changes (1)
  • pages/_document.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
scripts/tools/extract-tools-github.ts

[error] 18-18: 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/build-meetings.ts

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

types/packages/markdown-toc.d.ts

[error] 29-29: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

🪛 eslint
scripts/build-docs.ts

[error] 138-138: Use array destructuring.

(prefer-destructuring)


[error] 146-146: Expected '===' and instead saw '=='.

(eqeqeq)


[error] 147-147: Expected '===' and instead saw '=='.

(eqeqeq)

types/githubGraphQL.ts

[error] 76-76: Replace "Issue"·|·"PullRequest" with 'Issue'·|·'PullRequest'

(prettier/prettier)


[error] 76-76: Strings must use singlequote.

(quotes)


[error] 76-76: Strings must use singlequote.

(quotes)


[error] 95-95: Newline required at end of file but not found.

(eol-last)


[error] 95-95: Insert

(prettier/prettier)

utils/getStatic.ts

[error] 12-12: Delete ,

(prettier/prettier)


[error] 12-12: Unexpected trailing comma.

(comma-dangle)


[error] 13-13: Delete ,

(prettier/prettier)


[error] 13-13: Unexpected trailing comma.

(comma-dangle)


[error] 22-22: Delete ,

(prettier/prettier)


[error] 22-22: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Delete ,

(prettier/prettier)


[error] 34-34: Unexpected trailing comma.

(comma-dangle)


[error] 48-48: Delete ,

(prettier/prettier)


[error] 48-48: Unexpected trailing comma.

(comma-dangle)

utils/languageDetector.ts

[error] 7-7: Delete ,

(prettier/prettier)


[error] 7-7: Unexpected trailing comma.

(comma-dangle)

🔇 Additional comments (22)
scripts/build-rss.ts (2)

75-89: Double-check date conversions and time zones.
Ensure no discrepancies in date/time formatting or time zone issues if your environment is sensitive to time zone drift. Using library-based date formatting may be safer.


36-40: Validate the featured property existence before usage.
Currently, the code sorts based on the featured field. If featured is undefined for any post, it may lead to unintended sorting behaviors. Ensure fallback or default values.

scripts/tools/tools-object.ts (1)

60-74: Ensure error-logging doesn't mask critical issues.
The code gracefully logs problems when encountering invalid tool files, but consider whether continuing execution can lead to partial or incorrect final data.

scripts/tools/combine-tools.ts (2)

29-35: Consider a consistent ID or index approach for mapping.
Fuse is used repeatedly for searching color tags. Suppose multiple new tags appear; ensure all are assigned correct, unique identifiers.


139-158: Validate or safely fallback for tool.title.
Sorting by tool.title might lead to runtime errors if tool.title is missing. Ensure a fallback or a validation step prior to sorting.

scripts/dashboard/build-dashboard.ts (1)

33-38: Check for potential undefined “filter” usage or missing “/”
The function splits the label name on the “/” character. Consider adding a safety check or fallback default for cases where no “/” is present in the label string, to avoid possible runtime errors.

Run the following script to search for all usage patterns of this function to see if there’s ever a case where “filter” may be empty or not contain “/”:

scripts/build-post-list.ts (1)

61-68: Validate existing slugs before pushing
If there is any possibility of a repeated or malformed slug, it could cause collisions in finalResult arrays. Consider adding a uniqueness check to ensure no duplicates.

tsconfig.json (1)

19-20: Verify type definitions inclusion

The removal of "types/**/*.d.ts" from the include array might affect custom type definitions. Ensure all type definition files are still properly included through the existing patterns.

✅ Verification successful

The previous script showed that .d.ts files exist in the types directory, but we need to verify their content and usage to make a proper assessment. The new script will help us understand if these type definitions are actually being used in the codebase and their importance.


Type definitions are properly included in the configuration

The **/*.ts pattern in the include array already covers all TypeScript files, including .d.ts files, in any subdirectory. This is confirmed by:

  1. All .d.ts files are located under the types directory, which is covered by the **/*.ts pattern
  2. The type definitions are actively used in the codebase (e.g., SponsorType is used in multiple sponsor-related components)

The removal of the explicit "types/**/*.d.ts" pattern is redundant and doesn't affect type coverage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if any .d.ts files exist outside the included patterns
fd -e d.ts -E "node_modules" -E "netlify" | while read -r file; do
  if ! [[ "$file" =~ ^(next-env\.d\.ts|.*\.ts|.*\.tsx|.*\.json)$ ]]; then
    echo "Warning: Type definition file may be excluded: $file"
  fi
done

Length of output: 461


Script:

#!/bin/bash
# Let's check the actual content of the types directory to understand the structure
fd . "types" -t f -x echo "=== {} ===" \; cat "{}"

# Also check if these type definitions are actually used in the codebase
rg -l "SponsorType|jgexml__json2xml|markdown-toc" --type ts

Length of output: 1600

types/packages/markdown-toc.d.ts (1)

4-9: LGTM! Well-structured interface definition

The TocItem interface clearly defines all necessary properties with appropriate types.

types/scripts/build-rss.ts (1)

2-7: Review recursive type definition in Enclosure

The Enclosure type has a recursive definition that could lead to infinite nesting. Consider if this is intentional or if it should be limited.

Consider using a non-recursive structure or limiting the depth:

 export type Enclosure = {
   '@url': string;
   '@length': number;
   '@type': string;
-  enclosure: Enclosure;
+  enclosure?: Enclosure; // Make it optional
 };
scripts/casestudies/index.ts (1)

10-17: Good use of Promise.all for parallel processing

Excellent implementation of concurrent file processing using Promise.all. This effectively addresses the ESLint no-await-in-loop rule while improving performance.

scripts/dashboard/issue-queries.ts (1)

1-1: LGTM! Clean migration to ES module syntax.

The transition from CommonJS exports.Queries to ES modules export const Queries is correct and aligns with the TypeScript migration objective.

scripts/compose.ts (1)

10-16: LGTM! Well-structured type definition.

The ComposePromptType interface clearly defines the expected structure of the prompt answers.

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

Line range hint 85-108: LGTM! Well-structured markdown validation workflow

The markdown validation implementation with error capture and PR commenting is well-designed and provides good feedback to contributors.

package.json (2)

11-27: LGTM: Consistent migration to tsx for script execution

The script commands have been properly updated to use tsx instead of node, which aligns with the TypeScript migration strategy discussed in the PR comments.


118-118: LGTM: Appropriate TypeScript dependencies added

The addition of tsx and type definitions for fs-extra and inquirer properly supports the TypeScript migration.

Also applies to: 134-135

scripts/build-docs.ts (1)

93-116: LGTM: Well-structured recursive function

The convertDocPosts function is well-documented and properly handles the recursive traversal of the document tree.

scripts/tools/tools-schema.json (2)

43-110: LGTM: Comprehensive language support

The schema properly handles both single string and array values for languages, with well-documented explanations and examples.


145-200: LGTM: Well-structured categories definition

The categories property is well-defined with clear documentation, validation rules, and comprehensive examples.

.eslintrc (1)

317-322: Review the necessity of disabling JSDoc requirements

While disabling import/no-extraneous-dependencies and no-console is justified for build scripts (CLI tools), consider keeping require-jsdoc enabled to maintain documentation quality during the TypeScript migration.

Let's check the current JSDoc coverage in scripts:

Consider keeping JSDoc requirements for better maintainability, especially during the TypeScript migration phase where type documentation becomes more crucial.

utils/languageDetector.ts (1)

3-3: LGTM: CommonJS extension properly specified

The .cjs extension correctly identifies this as a CommonJS module, which is appropriate for configuration files in a TypeScript project.

utils/getStatic.ts (1)

3-3: LGTM: Consistent use of CommonJS extension

The .cjs extension usage is consistent with other files in the codebase.

Comment on lines +13 to +24
function clean(s: string) {
let cleanS = s;

cleanS = cleanS.split('&ltspan&gt').join('');
cleanS = cleanS.split('&amp').join('&');
cleanS = cleanS.split('&#39;').join("'");
cleanS = cleanS.split('&lt;').join('<');
cleanS = cleanS.split('&gt;').join('>');
cleanS = cleanS.split('&quot;').join('"');

return cleanS;
}
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 sanitizing more aggressively or using a template engine.
The current manual string replacements could miss an unhandled HTML entity. A robust HTML sanitization library may help avoid edge cases and XSS-like vulnerabilities.

Comment on lines +46 to +50
const getFinalTool = async (toolObject: AsyncAPITool) => {
const finalObject = toolObject;

// there might be a tool without language
if (toolObject.filters.language) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Make sure optional filters are properly handled.
The code only checks toolObject.filters.language and toolObject.filters.technology. If filters is undefined, this can break.

-if (toolObject.filters.language) {
+if (toolObject.filters?.language) {
📝 Committable suggestion

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

Suggested change
const getFinalTool = async (toolObject: AsyncAPITool) => {
const finalObject = toolObject;
// there might be a tool without language
if (toolObject.filters.language) {
const getFinalTool = async (toolObject: AsyncAPITool) => {
const finalObject = toolObject;
// there might be a tool without language
if (toolObject.filters?.language) {

Comment on lines +140 to +156
async function getHotDiscussions(discussions: HotDiscussionsIssuesNode[]) {
const result: ProcessedDiscussion[] = [];
const batchSize = 5;

for (let i = 0; i < discussions.length; i += batchSize) {
const batch = discussions.slice(i, i + batchSize);
// eslint-disable-next-line no-await-in-loop
const batchResults = await processHotDiscussions(batch);

// eslint-disable-next-line no-await-in-loop
await pause(1000);
result.push(...batchResults);
}

result.sort((ElemA, ElemB) => ElemB.score - ElemA.score);
const filteredResult = result.filter((issue) => issue.author !== 'asyncapi-bot');

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 concurrency approach for getHotDiscussions
Processing discussions in batches (line 145) is good, but consider adjusting the pause interval or using Promise.allSettled for improved throughput. If the GitHub rate limit is generous enough, you could parallelize some portion of these calls.

rename.ps1 Outdated
Comment on lines 1 to 14
# Define the root folder
$rootFolder = "scripts"

# Get all .js files recursively
$jsFiles = Get-ChildItem -Path $rootFolder -Recurse -Filter "*.js"

# Loop through each .js file and run the git mv command
foreach ($file in $jsFiles) {
# Construct the new file name with .ts extension
$newFileName = $file.FullName -replace "\.js$", ".ts"

# Execute the git mv command
git mv $file.FullName $newFileName
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and validation to the rename script

While the script's basic functionality works, it needs additional safeguards for production use:

  1. Validate file existence before renaming
  2. Handle git mv failures
  3. Verify genuine JS files (not just .js extension)
  4. Add a dry-run option

Here's a more robust implementation:

 # Define the root folder
 $rootFolder = "scripts"
+
+# Add dry-run parameter
+param(
+    [switch]$DryRun = $false
+)
+
+# Verify the root folder exists
+if (-not (Test-Path $rootFolder)) {
+    Write-Error "Root folder '$rootFolder' does not exist"
+    exit 1
+}
 
 # Get all .js files recursively
 $jsFiles = Get-ChildItem -Path $rootFolder -Recurse -Filter "*.js"
+
+# Verify we found files to process
+if ($jsFiles.Count -eq 0) {
+    Write-Warning "No .js files found in $rootFolder"
+    exit 0
+}
 
 # Loop through each .js file and run the git mv command
 foreach ($file in $jsFiles) {
+    # Verify it's actually a JavaScript file (not just .js extension)
+    $content = Get-Content $file.FullName -First 1
+    if (-not ($content -match '^(//|/\*|import\s|const\s|let\s|var\s|function\s|class\s)')) {
+        Write-Warning "Skipping $($file.FullName) - doesn't appear to be a JavaScript file"
+        continue
+    }
+
     # Construct the new file name with .ts extension
     $newFileName = $file.FullName -replace "\.js$", ".ts"
 
+    if ($DryRun) {
+        Write-Host "Would rename $($file.FullName) to $newFileName"
+        continue
+    }
+
     # Execute the git mv command
-    git mv $file.FullName $newFileName
+    $result = git mv $file.FullName $newFileName 2>&1
+    if ($LASTEXITCODE -ne 0) {
+        Write-Error "Failed to rename $($file.FullName): $result"
+        continue
+    }
+    Write-Host "Successfully renamed $($file.FullName) to $newFileName"
 }
📝 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
# Define the root folder
$rootFolder = "scripts"
# Get all .js files recursively
$jsFiles = Get-ChildItem -Path $rootFolder -Recurse -Filter "*.js"
# Loop through each .js file and run the git mv command
foreach ($file in $jsFiles) {
# Construct the new file name with .ts extension
$newFileName = $file.FullName -replace "\.js$", ".ts"
# Execute the git mv command
git mv $file.FullName $newFileName
}
# Define the root folder
$rootFolder = "scripts"
# Add dry-run parameter
param(
[switch]$DryRun = $false
)
# Verify the root folder exists
if (-not (Test-Path $rootFolder)) {
Write-Error "Root folder '$rootFolder' does not exist"
exit 1
}
# Get all .js files recursively
$jsFiles = Get-ChildItem -Path $rootFolder -Recurse -Filter "*.js"
# Verify we found files to process
if ($jsFiles.Count -eq 0) {
Write-Warning "No .js files found in $rootFolder"
exit 0
}
# Loop through each .js file and run the git mv command
foreach ($file in $jsFiles) {
# Verify it's actually a JavaScript file (not just .js extension)
$content = Get-Content $file.FullName -First 1
if (-not ($content -match '^(//|/\*|import\s|const\s|let\s|var\s|function\s|class\s)')) {
Write-Warning "Skipping $($file.FullName) - doesn't appear to be a JavaScript file"
continue
}
# Construct the new file name with .ts extension
$newFileName = $file.FullName -replace "\.js$", ".ts"
if ($DryRun) {
Write-Host "Would rename $($file.FullName) to $newFileName"
continue
}
# Execute the git mv command
$result = git mv $file.FullName $newFileName 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to rename $($file.FullName): $result"
continue
}
Write-Host "Successfully renamed $($file.FullName) to $newFileName"
}

Comment on lines +1 to +3
import axios from 'axios';
import dotenv from 'dotenv';

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Verify environment variable presence

Add validation for the required GITHUB_TOKEN environment variable:

 import axios from 'axios';
 import dotenv from 'dotenv';
 
 dotenv.config();
+
+if (!process.env.GITHUB_TOKEN) {
+  throw new Error('GITHUB_TOKEN environment variable is required');
+}
📝 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
import axios from 'axios';
import dotenv from 'dotenv';
import axios from 'axios';
import dotenv from 'dotenv';
dotenv.config();
if (!process.env.GITHUB_TOKEN) {
throw new Error('GITHUB_TOKEN environment variable is required');
}

Comment on lines +154 to +157
name: 'Scala',
color: 'bg-[#D7C7F2]',
borderColor: 'border-[#A387D2]'
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate Scala entry

Scala is already defined in the languagesColor array (lines 51-54). It should not appear in the technologiesColor array.

-  {
-    name: 'Scala',
-    color: 'bg-[#D7C7F2]',
-    borderColor: 'border-[#A387D2]'
-  },

Comment on lines +70 to +72
- if: steps.packagejson.outputs.exists == 'true'
name: Install dependencies
run: npm install
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider using npm ci instead of npm install

The change from npm ci to npm install might lead to inconsistent dependency installations across different environments. npm ci is preferred in CI environments as it:

  • Uses package-lock.json exclusively
  • Is more reliable for reproducible builds
  • Removes node_modules before installation
- run: npm install
+ run: npm ci
📝 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: steps.packagejson.outputs.exists == 'true'
name: Install dependencies
run: npm install
- if: steps.packagejson.outputs.exists == 'true'
name: Install dependencies
run: npm ci

Comment on lines 146 to 147
if (post?.isRootSection || post?.isSection || index == 0) {
if (post?.isRootSection || index == 0)
rootSections.push(post.title)
return post
if (post?.isRootSection || index == 0) rootSections.push(post.title);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix loose equality comparisons

Replace loose equality (==) with strict equality (===) for type-safe comparisons.

-if (post?.isRootSection || post?.isSection || index == 0) {
-  if (post?.isRootSection || index == 0) rootSections.push(post.title);
+if (post?.isRootSection || post?.isSection || index === 0) {
+  if (post?.isRootSection || index === 0) rootSections.push(post.title);
📝 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 (post?.isRootSection || post?.isSection || index == 0) {
if (post?.isRootSection || index == 0)
rootSections.push(post.title)
return post
if (post?.isRootSection || index == 0) rootSections.push(post.title);
if (post?.isRootSection || post?.isSection || index === 0) {
if (post?.isRootSection || index === 0) rootSections.push(post.title);
🧰 Tools
🪛 eslint

[error] 146-146: Expected '===' and instead saw '=='.

(eqeqeq)


[error] 147-147: Expected '===' and instead saw '=='.

(eqeqeq)


export default languageDetector({
supportedLngs: i18nextConfig.i18n.locales,
fallbackLng: i18nextConfig.i18n.defaultLocale
fallbackLng: i18nextConfig.i18n.defaultLocale,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove trailing comma to match ESLint rules

The trailing comma conflicts with the project's ESLint configuration which has comma-dangle set to "never".

Apply this fix:

-  fallbackLng: i18nextConfig.i18n.defaultLocale,
+  fallbackLng: i18nextConfig.i18n.defaultLocale
📝 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
fallbackLng: i18nextConfig.i18n.defaultLocale,
fallbackLng: i18nextConfig.i18n.defaultLocale
🧰 Tools
🪛 eslint

[error] 7-7: Delete ,

(prettier/prettier)


[error] 7-7: Unexpected trailing comma.

(comma-dangle)

Comment on lines +12 to +13
lang: lng,
},
Copy link

@coderabbitai coderabbitai bot Dec 21, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove trailing commas to match ESLint rules

Multiple trailing commas have been added that conflict with the project's ESLint configuration.

Apply these fixes:

-      lang: lng,
-    },
+      lang: lng
+    }

-  paths: getI18nPaths(),
+  paths: getI18nPaths()

-    ...(await serverSideTranslations(locale, ns)),
+    ...(await serverSideTranslations(locale, ns))

-      props: await getI18nProps(ctx, ns as any),
+      props: await getI18nProps(ctx, ns as any)

Also applies to: 22-22, 34-34, 48-48

🧰 Tools
🪛 eslint

[error] 12-12: Delete ,

(prettier/prettier)


[error] 12-12: Unexpected trailing comma.

(comma-dangle)


[error] 13-13: Delete ,

(prettier/prettier)


[error] 13-13: Unexpected trailing comma.

(comma-dangle)

Copy link
Member

Choose a reason for hiding this comment

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

We haven't added trailingComma config in eslintrc. Why are you suggesting trailing commas?

Copy link

Choose a reason for hiding this comment

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

@akshatnema, it looks like the ESLint/Prettier configuration may be pulling in a “trailingComma” rule from shared or default settings, even if it’s not explicitly listed in your .eslintrc file. Sometimes frameworks or recommended configs bundle these rules by default. If you prefer to allow or enforce trailing commas, you can override that rule locally in your ESLint or Prettier configuration. Otherwise, you can remove any trailing commas to avoid the warnings.

@akshatnema
Copy link
Member

@coderabbitai review

Copy link

coderabbitai bot commented Dec 22, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 9

🔭 Outside diff range comments (6)
scripts/index.ts (2)

Line range hint 13-47: Add type annotations and error handling to the start function

The start function would benefit from proper TypeScript annotations and error handling for build steps.

Consider this improvement:

-async function start() {
+async function start(): Promise<void> {
   try {
     await buildPostList();
-    rssFeed('blog', 'AsyncAPI Initiative Blog RSS Feed', 'AsyncAPI Initiative Blog', 'rss.xml');
+    await rssFeed('blog', 'AsyncAPI Initiative Blog RSS Feed', 'AsyncAPI Initiative Blog', 'rss.xml');
     await buildCaseStudiesList('config/casestudies', resolve(currentDirPath, '../config', 'case-studies.json'));
     await buildAdoptersList();
     const financeDir = resolve('.', 'config', 'finance');
     // ... rest of the function
   } catch (error) {
     console.error('Failed to build site:', error);
     process.exit(1);
   }
 }

Line range hint 48-51: Prevent race condition in module execution

The immediate execution of start() after export could cause issues in testing or when importing this module.

Consider this pattern:

 export { start };

-start();
+if (process.argv[1] === fileURLToPath(import.meta.url)) {
+  start().catch((error) => {
+    console.error('Failed to build site:', error);
+    process.exit(1);
+  });
+}
scripts/build-pages.ts (2)

Line range hint 24-49: Add error handling to file system operations

The copyAndRenameFiles function should handle potential file system errors.

-export function copyAndRenameFiles(srcDir: string, targetDir: string) {
+export function copyAndRenameFiles(srcDir: string, targetDir: string): void {
+  try {
     const entries = fs.readdirSync(srcDir, { withFileTypes: true });
     // ... rest of the function
+  } catch (error) {
+    console.error(`Failed to process directory ${srcDir}:`, error);
+    throw error;
+  }
 }

Line range hint 51-51: Add execution guard for direct script running

Similar to other scripts, add a guard for direct execution.

-copyAndRenameFiles(SRC_DIR, TARGET_DIR);
+if (process.argv[1] === fileURLToPath(import.meta.url)) {
+  try {
+    copyAndRenameFiles(SRC_DIR, TARGET_DIR);
+  } catch (error) {
+    console.error('Failed to build pages:', error);
+    process.exit(1);
+  }
+}
scripts/dashboard/issue-queries.ts (1)

Consider implementing pagination for comments query

Based on the verification results, there are issues in the AsyncAPI organization with significant comment counts (one issue has 376 comments, another has 125). With the current implementation using comments(last: 100), some comments would be missed for issues with more than 100 comments. Implement cursor-based pagination to ensure all comments are retrieved.

  • scripts/dashboard/issue-queries.ts: Update the comments query to use pagination with cursor
    comments(last: 100, before: $cursor) {
      pageInfo {
        hasPreviousPage
        startCursor
      }
      nodes {
        ...CommentFields
      }
    }
🔗 Analysis chain

Line range hint 73-73: Review pagination settings

The comments(last: 100) in the issueById query might hit rate limits with large repositories. Consider implementing pagination or reducing the page size.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are issues with many comments that might hit rate limits
gh api graphql -f query='
query {
  search(query: "org:asyncapi is:issue comments:>100", type: ISSUE, first: 5) {
    nodes {
      ... on Issue {
        number
        comments {
          totalCount
        }
      }
    }
  }
}'

Length of output: 357


Script:

#!/bin/bash
# Let's check the actual query implementation to understand the context better
rg -U "issueById.*comments.*last:" --type ts

Length of output: 46


Script:

#!/bin/bash
# Let's try a broader search to find the GraphQL query implementation
rg -U "comments.*last:" --type ts

Length of output: 98

scripts/build-docs.ts (1)

Line range hint 1-205: Consider adding TypeScript interfaces

Since we're migrating to TypeScript, consider adding interfaces to improve type safety and code documentation:

interface NavItem {
  title: string;
  weight: number;
  isRootSection?: boolean;
  isSection?: boolean;
  rootSectionId?: string;
  sectionWeight?: number;
  slug: string;
  parent?: string;
  sectionId?: string;
  isPrerelease?: boolean;
}

interface NavTree {
  [key: string]: {
    item: NavItem;
    children: {
      [key: string]: {
        item: NavItem;
        children: NavItem[];
      };
    };
  };
}
🧰 Tools
🪛 eslint

[error] 80-82: Blocks are nested too deeply (5). Maximum allowed is 4.

(max-depth)


[error] 138-138: Use array destructuring.

(prefer-destructuring)


[error] 146-146: Expected '===' and instead saw '=='.

(eqeqeq)


[error] 147-147: Expected '===' and instead saw '=='.

(eqeqeq)

♻️ Duplicate comments (5)
scripts/casestudies/index.ts (1)

12-12: 🛠️ Refactor suggestion

Use path.join for cross-platform compatibility

Using string concatenation for path joining can lead to issues across different operating systems.

+import { join } from 'path';
 // ...
-const caseStudyFileName = [dirWithCaseStudy, file].join('/');
+const caseStudyFileName = join(dirWithCaseStudy, file);
scripts/utils.ts (1)

3-8: 🛠️ Refactor suggestion

Improve type safety with explicit types

The function should have explicit return type and input type annotations.

-function convertToJson(contentYAMLorJSON: string) {
+function convertToJson(contentYAMLorJSON: string | unknown): unknown {
scripts/build-tools.ts (1)

20-21: ⚠️ Potential issue

Replace dynamic require with static import

Using dynamic require with eslint-disable is not recommended in TypeScript/ESM.

Consider using fs.readFile for JSON:

-    // eslint-disable-next-line import/no-dynamic-require, global-require
-    await combineTools(automatedTools, require(manualToolsPath), toolsPath, tagsPath);
+    const manualTools = JSON.parse(await fs.readFile(manualToolsPath, 'utf-8'));
+    await combineTools(automatedTools, manualTools, toolsPath, tagsPath);
utils/languageDetector.ts (1)

7-7: ⚠️ Potential issue

Remove trailing comma to match ESLint rules

The trailing comma conflicts with the project's ESLint configuration which has comma-dangle set to "never".

-  fallbackLng: i18nextConfig.i18n.defaultLocale,
+  fallbackLng: i18nextConfig.i18n.defaultLocale
🧰 Tools
🪛 eslint

[error] 7-7: Delete ,

(prettier/prettier)


[error] 7-7: Unexpected trailing comma.

(comma-dangle)

utils/getStatic.ts (1)

12-13: ⚠️ Potential issue

Remove trailing commas to comply with ESLint rules

Multiple trailing commas have been added that conflict with the project's ESLint configuration. As discussed in previous comments, the project doesn't have explicit trailing comma configuration.

Apply these fixes:

-      lang: lng,
-    },
+      lang: lng
+    }

-  paths: getI18nPaths(),
+  paths: getI18nPaths()

-    ...(await serverSideTranslations(locale, ns)),
+    ...(await serverSideTranslations(locale, ns))

-      props: await getI18nProps(ctx, ns as any),
+      props: await getI18nProps(ctx, ns as any)

Also applies to: 22-22, 34-34, 48-48

🧰 Tools
🪛 eslint

[error] 12-12: Delete ,

(prettier/prettier)


[error] 12-12: Unexpected trailing comma.

(comma-dangle)


[error] 13-13: Delete ,

(prettier/prettier)


[error] 13-13: Unexpected trailing comma.

(comma-dangle)

🧹 Nitpick comments (38)
scripts/build-newsroom-videos.ts (1)

17-63: Optional: Remove console logs or conditionally enable them.
Repeated console logs (lines 34 and 55) might clutter production logs. Consider removing or using a logger with adjustable log levels.

scripts/build-rss.ts (1)

90-108: Optional: Improve enclosure defaults.
Currently, a single dummy value is used for enclosure lengths (line 94). Consider deriving a real file size to improve RSS clients’ accuracy.

scripts/tools/tools-object.ts (1)

89-119: Drop or handle invalid tools more gracefully.
Logging invalid tool files is good, but consider storing them in a separate “invalidTools” list or continuing with safe defaults. Keeping partial data and logs might be confusing for end users.

scripts/markdown/check-markdown.ts (4)

10-19: Ensure edge cases around “isValidURL” are handled.
Your URL check is simple and effective, but consider edge cases like empty strings or protocol-less URLs (e.g., "www.example.com"). It may be beneficial to return false for strings that do not contain a protocol or domain.


31-40: Handle missing required attributes more robustly.
If a blog post does not contain one of the required attributes, you currently push an error message but keep going. Consider returning early or adding more context to the error message (e.g., file name). This ensures quicker debugging and reduces the risk of incomplete data.


58-74: Validate the photo attribute similar to link validation.
You're already validating that 'link' is a valid URL. It might be useful to do the same for the 'photo' field, as broken or invalid URLs for author photos can lead to rendering issues.


95-122: Optimize for concurrent file reads.
Currently, we load each file’s content sequentially. You are using Promise.all in some places, but a deeper concurrency approach (like processing each file in parallel up front) may speed up validation, especially for large directories.

scripts/tools/combine-tools.ts (1)

165-167: Maintain a single source for writing languages & technologies.
You’re writing two separate JSON files (tools and tags). If future changes occur, ensure consistent synchronization. You might consider storing them in a single structure if these always need to be updated together.

scripts/dashboard/build-dashboard.ts (4)

25-31: Potential for date parsing errors.
“monthsSince” function relies on a properly formatted date. If the string cannot be parsed as a date, new Date('…') may produce unexpected results. Consider validating or throwing an error when the date is invalid.


70-77: Cascading recursion in getDiscussions may increase call depth.
The function calls itself recursively when hasNextPage is true. For large result sets, consider an iterative approach (while loop) to reduce the risk of a deep call stack.


Line range hint 96-139: Consider partial concurrency in processHotDiscussions.
This function processes discussions in batches of 5, then pauses for 1 second. If the rate limit constraints allow, you could reduce or remove the pause for improved throughput, or use a dynamic pause approach based on actual rateLimit data.


Line range hint 160-174: Capture more detail in error logs.
When writing to file fails, you log only the error’s message. Consider logging the entire error stack or relevant code snippet for deeper debugging.

scripts/build-post-list.ts (4)

61-68: Ensure consistent classification for new sections.
When “details.slug” starts with “/about,” you place it into finalResult.about, but in future expansions (like new sections), you’d need an additional condition. Consider a more flexible approach (e.g., a lookup table) to handle future top-level sections.


95-103: Consider validating the _section.mdx file if it exists.
You do parse the frontMatter from “_section.mdx,” but you accept it even if mandatory fields are missing. For more consistency, you may want to reuse an existing validator (similar to other frontMatter validations).


123-126: Limit excerpt length gracefully.
Truncating to 200 chars might cut words abruptly. Consider using a word-based approach or adding ellipses (…) to indicate the text is longer.


178-188: Production environment logging.
You are suppressing console output in production. Verify if you need a debug or trace logging approach for diagnosing issues in a real environment.

types/packages/jgexml__json2xml.d.ts (1)

6-8: Consider using ES module export syntax.

Since this is part of a TypeScript migration, consider using modern ES module syntax:

-  const json2xml: Json2Xml;
-  export = json2xml;
+  const json2xml: Json2Xml;
+  export default json2xml;
next-i18next.config.cjs (1)

11-11: Improve inline comment documentation.

The comment "this line" doesn't provide any context about why useSuspense is set to false.

-    react: { useSuspense: false } // this line
+    react: { useSuspense: false } // Disable Suspense to prevent hydration issues with SSR
types/packages/markdown-toc.d.ts (1)

1-1: Update file path comment

The comment indicates src/types/markdown-toc.d.ts but the file is located at types/packages/markdown-toc.d.ts.

-// src/types/markdown-toc.d.ts
+// types/packages/markdown-toc.d.ts
types/scripts/build-posts-list.ts (1)

7-27: Consider constraining the index signature type

The index signature [key: string]: any allows any type of value for additional properties, which reduces TypeScript's type safety benefits.

Consider constraining the type to more specific types that might be needed:

-  [key: string]: any; // For any additional properties
+  [key: string]: string | number | boolean | undefined; // For any additional properties
scripts/utils/readAndWriteJson.ts (1)

5-29: Enhance error handling and add return type

The function implementation is good, but could be improved in several ways:

  1. Add explicit return type
  2. Include more specific error context
  3. Add formatting to JSON output
-export async function writeJSON(readPath: string, writePath: string) {
+export async function writeJSON(readPath: string, writePath: string): Promise<void> {
   let readContent;
   let jsonContent;

   // Attempt to read the file
   try {
     readContent = await readFile(readPath, 'utf-8');
   } catch (err) {
-    throw new Error(`Error while reading file\nError: ${err}`);
+    throw new Error(`Failed to read file from ${readPath}\nError: ${err}`);
   }

   // Attempt to convert content to JSON
   try {
     jsonContent = convertToJson(readContent);
   } catch (err) {
-    throw new Error(`Error while conversion\nError: ${err}`);
+    throw new Error(`Failed to convert content from ${readPath} to JSON\nError: ${err}`);
   }

   // Attempt to write the JSON content to file
   try {
-    await writeFile(writePath, JSON.stringify(jsonContent));
+    await writeFile(writePath, JSON.stringify(jsonContent, null, 2));
   } catch (err) {
-    throw new Error(`Error while writing file\nError: ${err}`);
+    throw new Error(`Failed to write JSON to ${writePath}\nError: ${err}`);
   }
 }
types/scripts/build-rss.ts (1)

18-37: Consider extracting XML attribute types

The RSS type effectively captures the RSS structure, but could be more maintainable by extracting common XML attribute patterns.

Consider creating a reusable type for XML attributes:

type XMLAttribute = `@${string}`;
type XMLAttributes = {
  [K in XMLAttribute]: string;
};

export type RSS = XMLAttributes & {
  channel: {
    // ... rest of the type definition
  };
};
scripts/casestudies/index.ts (2)

5-5: Add return type annotation for better type safety

The function should explicitly declare its return type for better type safety and documentation.

-export async function buildCaseStudiesList(dirWithCaseStudy: string, writeFilePath: string) {
+export async function buildCaseStudiesList(dirWithCaseStudy: string, writeFilePath: string): Promise<unknown[]> {

Also applies to: 22-22


23-25: Enhance error handling with specific error types

The current error handling loses the stack trace and type information. Consider preserving the original error or using a custom error class.

-  } catch (err) {
-    throw new Error(err instanceof Error ? err.message : String(err));
+  } catch (err) {
+    if (err instanceof Error) {
+      throw err;
+    }
+    throw new Error(String(err));
   }
scripts/utils.ts (1)

23-23: Structure error messages for better debugging

The current error message concatenation makes it difficult to programmatically handle different error types.

-      throw new Error(`Invalid content format:\nJSON Parse Error: ${jsonError}\nYAML Parse Error: ${yamlError}`);
+      throw new Error(JSON.stringify({
+        message: 'Invalid content format',
+        jsonError: jsonError instanceof Error ? jsonError.message : String(jsonError),
+        yamlError: yamlError instanceof Error ? yamlError.message : String(yamlError)
+      }));
scripts/finance/index.ts (1)

23-24: Consider centralizing path configurations

File paths and names are hardcoded throughout the function. Consider moving these to a configuration object or constants.

+const FINANCE_FILES = {
+  expenses: {
+    source: 'Expenses.yml',
+    target: 'Expenses.json'
+  },
+  expensesLink: {
+    source: 'ExpensesLink.yml',
+    target: 'ExpensesLink.json'
+  }
+} as const;

Also applies to: 32-33, 36-37

scripts/build-tools.ts (1)

13-26: Add return type annotation to buildTools function

The function's return type should be explicitly declared for better type safety.

-const buildTools = async (automatedToolsPath: string, manualToolsPath: string, toolsPath: string, tagsPath: string) => {
+const buildTools = async (automatedToolsPath: string, manualToolsPath: string, toolsPath: string, tagsPath: string): Promise<void> => {
scripts/build-pages.ts (1)

Line range hint 4-8: Add type annotations to constants

The constants would benefit from explicit type annotations.

-const SRC_DIR = 'markdown';
-const TARGET_DIR = 'pages';
-const capitalizeTags = ['table', 'tr', 'td', 'th', 'thead', 'tbody'];
+const SRC_DIR: string = 'markdown';
+const TARGET_DIR: string = 'pages';
+const capitalizeTags: readonly string[] = ['table', 'tr', 'td', 'th', 'thead', 'tbody'] as const;
types/scripts/tools.ts (1)

7-25: Consider using an enum for Category type

While the union type works, using an enum would provide better type safety and maintainability.

-type Category =
-  | 'api'
-  | 'code-first'
-  // ... other categories
-  | 'ide-extension';
+enum Category {
+  API = 'api',
+  CODE_FIRST = 'code-first',
+  // ... other categories
+  IDE_EXTENSION = 'ide-extension'
+}
scripts/build-meetings.ts (1)

42-44: Consider using type guard for event validation

Instead of throwing an error, consider using a type guard to filter out invalid events.

-if (!e.start || !e.start.dateTime) {
-  throw new Error('start.dateTime is missing in the event');
-}
+const isValidEvent = (e: any): e is { start: { dateTime: string } } => {
+  return e?.start?.dateTime != null;
+};
+
+eventsItems = eventsList.data.items
+  .filter(isValidEvent)
+  .map((e) => {
scripts/tools/tags-color.ts (3)

164-172: Standardize color schemes for technologies

Jenkins and Flask are using the same color scheme as Kubernetes-native. Consider using distinct colors for better visual differentiation.

  {
    name: 'Jenkins',
-   color: 'bg-[#D7C7F2]',
-   borderColor: 'border-[#A387D2]'
+   color: 'bg-[#335061]',
+   borderColor: 'border-[#D33833]'
  },
  {
    name: 'Flask',
-   color: 'bg-[#D7C7F2]',
-   borderColor: 'border-[#A387D2]'
+   color: 'bg-[#FFFFFF]',
+   borderColor: 'border-[#000000]'
  }

6-6: Standardize language naming

For consistency, consider using just 'Go' instead of 'Go/Golang' as other language names use their primary name.

-    name: 'Go/Golang',
+    name: 'Go',

1-1: Consider enhancing type safety

Consider using string literals for the name field in LanguageColorItem type to prevent typos and ensure consistency.

type LanguageName = 'Go' | 'Java' | 'JavaScript' | /* ... other languages ... */;
type TechnologyName = 'Node.js' | 'Hermes' | /* ... other technologies ... */;
type ColorItem<T extends string> = {
  name: T;
  color: string;
  borderColor: string;
};
scripts/dashboard/issue-queries.ts (1)

1-1: Consider adding TypeScript types for query responses

Add TypeScript interfaces for the GraphQL query responses to improve type safety and developer experience.

interface PullRequestResponse {
  node: {
    __typename: 'PullRequest';
    assignees: {
      totalCount: number;
    };
    // ... other fields
  };
  rateLimit: RateLimit;
}

export const Queries = Object.freeze({
  pullRequestById: /* GraphQL */ `...` as const,
  // ... other queries
});
scripts/compose.ts (1)

19-23: Add input validation for tags

Consider adding validation for the tags input to ensure they meet your formatting requirements.

   const tagArray = answers.tags.split(',');
+  const validTagRegex = /^[a-z0-9-]+$/i;
 
   tagArray.forEach((tag: string, index: number) => {
-    tagArray[index] = tag.trim();
+    const trimmedTag = tag.trim();
+    if (!validTagRegex.test(trimmedTag)) {
+      throw new Error(`Invalid tag format: ${trimmedTag}`);
+    }
+    tagArray[index] = trimmedTag;
   });
scripts/build-docs.ts (1)

113-115: Improve error handling

The error messages could be more descriptive by including the original error details:

-    throw new Error('Error in convertDocPosts:', err);
+    throw new Error(`Error in convertDocPosts: ${err.message}`);
-    throw new Error('An error occurred while adding doc buttons:', err);
+    throw new Error(`An error occurred while adding doc buttons: ${err.message}`);

Also applies to: 199-200

scripts/tools/tools-schema.json (2)

14-15: Fix grammatical errors in description

The description contains grammatical errors that should be corrected for better clarity.

-"description": "By default scripts read description of repository there project is stored. You can override this behaviour by providing custom description."
+"description": "By default, scripts read the description from the repository where the project is stored. You can override this behavior by providing a custom description."

147-147: Fix typo in categories description

The description contains a grammatical error that affects readability.

-"description": "Categories are used to group tools by different use case, like documentation or code generation. If have a list of fixed categories. If you use different one that your tool lands under \"other\" category. Feel free to add your category through a pull request to AsyncAPI website repository.",
+"description": "Categories are used to group tools by different use cases, like documentation or code generation. We have a list of fixed categories. If you use a different one, your tool will land under the \"other\" category. Feel free to add your category through a pull request to the AsyncAPI website repository."
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9c1fda0 and b96f142.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (53)
  • .eslintrc (1 hunks)
  • .github/workflows/if-nodejs-pr-testing.yml (1 hunks)
  • next-i18next.config.cjs (1 hunks)
  • next-i18next.config.js (0 hunks)
  • package.json (3 hunks)
  • pages/_document.tsx (1 hunks)
  • scripts/adopters/index.js (0 hunks)
  • scripts/adopters/index.ts (1 hunks)
  • scripts/build-docs.ts (7 hunks)
  • scripts/build-meetings.ts (2 hunks)
  • scripts/build-newsroom-videos.js (0 hunks)
  • scripts/build-newsroom-videos.ts (1 hunks)
  • scripts/build-pages.ts (2 hunks)
  • scripts/build-post-list.js (0 hunks)
  • scripts/build-post-list.ts (1 hunks)
  • scripts/build-rss.js (0 hunks)
  • scripts/build-rss.ts (1 hunks)
  • scripts/build-tools.js (0 hunks)
  • scripts/build-tools.ts (1 hunks)
  • scripts/casestudies/index.js (0 hunks)
  • scripts/casestudies/index.ts (1 hunks)
  • scripts/compose.ts (2 hunks)
  • scripts/dashboard/build-dashboard.ts (9 hunks)
  • scripts/dashboard/issue-queries.ts (2 hunks)
  • scripts/finance/index.js (0 hunks)
  • scripts/finance/index.ts (1 hunks)
  • scripts/index.ts (2 hunks)
  • scripts/markdown/check-markdown.js (0 hunks)
  • scripts/markdown/check-markdown.ts (1 hunks)
  • scripts/tools/categorylist.js (0 hunks)
  • scripts/tools/categorylist.ts (1 hunks)
  • scripts/tools/combine-tools.js (0 hunks)
  • scripts/tools/combine-tools.ts (1 hunks)
  • scripts/tools/extract-tools-github.js (0 hunks)
  • scripts/tools/extract-tools-github.ts (1 hunks)
  • scripts/tools/tags-color.js (0 hunks)
  • scripts/tools/tags-color.ts (1 hunks)
  • scripts/tools/tools-object.js (0 hunks)
  • scripts/tools/tools-object.ts (1 hunks)
  • scripts/tools/tools-schema.json (1 hunks)
  • scripts/utils.js (0 hunks)
  • scripts/utils.ts (1 hunks)
  • scripts/utils/readAndWriteJson.js (0 hunks)
  • scripts/utils/readAndWriteJson.ts (1 hunks)
  • tsconfig.json (1 hunks)
  • types/packages/jgexml__json2xml.d.ts (1 hunks)
  • types/packages/markdown-toc.d.ts (1 hunks)
  • types/scripts/build-posts-list.ts (1 hunks)
  • types/scripts/build-rss.ts (1 hunks)
  • types/scripts/dashboard.ts (1 hunks)
  • types/scripts/tools.ts (1 hunks)
  • utils/getStatic.ts (5 hunks)
  • utils/languageDetector.ts (1 hunks)
💤 Files with no reviewable changes (16)
  • scripts/adopters/index.js
  • scripts/tools/extract-tools-github.js
  • next-i18next.config.js
  • scripts/tools/categorylist.js
  • scripts/utils.js
  • scripts/build-tools.js
  • scripts/tools/combine-tools.js
  • scripts/tools/tags-color.js
  • scripts/utils/readAndWriteJson.js
  • scripts/build-post-list.js
  • scripts/casestudies/index.js
  • scripts/build-rss.js
  • scripts/tools/tools-object.js
  • scripts/build-newsroom-videos.js
  • scripts/markdown/check-markdown.js
  • scripts/finance/index.js
✅ Files skipped from review due to trivial changes (1)
  • pages/_document.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
scripts/tools/extract-tools-github.ts

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

types/packages/markdown-toc.d.ts

[error] 29-29: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

scripts/build-meetings.ts

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 eslint
scripts/build-docs.ts

[error] 138-138: Use array destructuring.

(prefer-destructuring)


[error] 146-146: Expected '===' and instead saw '=='.

(eqeqeq)


[error] 147-147: Expected '===' and instead saw '=='.

(eqeqeq)

utils/getStatic.ts

[error] 12-12: Delete ,

(prettier/prettier)


[error] 12-12: Unexpected trailing comma.

(comma-dangle)


[error] 13-13: Delete ,

(prettier/prettier)


[error] 13-13: Unexpected trailing comma.

(comma-dangle)


[error] 22-22: Delete ,

(prettier/prettier)


[error] 22-22: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Delete ,

(prettier/prettier)


[error] 34-34: Unexpected trailing comma.

(comma-dangle)


[error] 48-48: Delete ,

(prettier/prettier)


[error] 48-48: Unexpected trailing comma.

(comma-dangle)

utils/languageDetector.ts

[error] 7-7: Delete ,

(prettier/prettier)


[error] 7-7: Unexpected trailing comma.

(comma-dangle)

🔇 Additional comments (32)
scripts/build-newsroom-videos.ts (2)

12-15: Check for undefined environment variables.
Please ensure that "YOUTUBE_TOKEN" is present before creating the YouTube client.


46-50: Use type guards instead of non-null assertions.
Applying type guards or optional chaining will make the code more robust.

types/scripts/dashboard.ts (1)

107-139: Reduce duplication via interface inheritance.
“HotDiscussionsIssuesNode” and “HotDiscussionsPullRequestsNode” repeat many properties. Extending from a base interface can improve maintainability.

scripts/build-rss.ts (1)

13-24: Consider a more robust sanitization approach.
Manual string replacements may miss edge cases or lead to partial sanitization. A templating or sanitization library might reduce XSS risks.

scripts/tools/tools-object.ts (1)

80-86: Validate URL references.
You rely on GitHub’s raw URL for the tool file (line 81). Ensure all fetch failures are handled to avoid partial tool listings.

scripts/markdown/check-markdown.ts (1)

143-161: Exit code logic is well-structured.
The approach of setting process.exit(1) only after all checks have run allows for complete error logging. This ensures thorough scanning through the entire folder, which is beneficial for debugging.

scripts/tools/combine-tools.ts (2)

46-50: Ensure filters are defined before accessing properties.
This was noted in a previous review: if toolObject.filters is undefined, referencing toolObject.filters.language can cause runtime errors. Add a guard check:

-if (toolObject.filters.language) {
+if (toolObject.filters?.language) {

124-157: Validation approach is non-blocking, which is great.
The script continues execution even if some manual tools fail schema validation. This is good for batch processing. Just make sure you can easily track any invalid tool entries so they can be fixed before going live.

scripts/dashboard/build-dashboard.ts (1)

Line range hint 179-193: mapGoodFirstIssues logic is concise.
The function’s filtering approach for labels is clear and straightforward. This is a well-structured transformation, reducing overhead for later code.

scripts/build-post-list.ts (1)

37-52: Confirm correct slug generation for headings.
By design, the “slugifyToC” function tries to parse inline heading IDs first, then fallback to default slugify logic. Ensure no collisions occur if multiple headings share the same text but differ only by custom anchors.

Please run this script to check for duplicate headings with different anchors:

✅ Verification successful

Heading slug generation implementation is safe and working as expected.

The verification shows that:

  1. The codebase uses <a name="..."/> style anchors consistently, primarily in specification documentation.
  2. No instances of {#...} style heading IDs were found, but the code safely handles this pattern if introduced in the future.
  3. All found anchor names follow consistent patterns (alphanumeric with hyphens/underscores) that are compatible with the slugify function.
  4. No cases of duplicate heading text with different anchors were found that could cause collisions.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# This script searches for headings in .mdx files that might produce the same slug
rg "^#{1,6} .*\{#.*\}" -A1

Length of output: 28


Script:

#!/bin/bash
# Let's try a broader search for both heading patterns mentioned in the code
# Pattern 1: Headings with {#...} style IDs
rg "^#{1,6}.*\{#.*\}" --type md --type mdx

# Pattern 2: Headings with <a name="..."/> style anchors
rg "^#{1,6}.*<a.*name=" --type md --type mdx

# Also search for any markdown files to confirm they exist
fd "\.(md|mdx)$"

Length of output: 11112


Script:

#!/bin/bash
# Let's try the search without file type restrictions since we found the markdown files
# Pattern 1: Headings with {#...} style IDs
rg "^#{1,6}.*\{#.*\}" 

# Pattern 2: Headings with <a name="..."/> style anchors
rg "^#{1,6}.*<a.*name=" 

# Let's also check the actual implementation of slugify function that's being used as fallback
rg -l "slugify.*\{.*firsth1.*maxdepth"

Length of output: 6878

types/packages/jgexml__json2xml.d.ts (1)

1-4: LGTM! Well-structured type declaration.

The interface definition is clear and type-safe. Good use of unknown type for the feed parameter instead of any, following TypeScript best practices.

next-i18next.config.cjs (1)

1-3: LGTM! Well-documented configuration setup.

The comment clearly explains the reason for the .cjs extension and includes a reference link for future maintainers.

scripts/tools/extract-tools-github.ts (2)

1-4: Verify environment variable presence

The environment variable validation is still missing.


6-20: Add TypeScript types and improve error handling

The function still needs proper TypeScript types and better error handling.

🧰 Tools
🪛 Biome (1.9.4)

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

tsconfig.json (1)

21-24: Review module resolution strategy

The moduleResolution: "bundler" in compilerOptions might conflict with ts-node's experimentalSpecifierResolution: "node". Consider using consistent resolution strategies or adding a separate tsconfig for ts-node.

types/packages/markdown-toc.d.ts (1)

28-30: Replace broad 'Object' type with specific interface

The options parameter type should be more specific to improve type safety.

🧰 Tools
🪛 Biome (1.9.4)

[error] 29-29: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

types/scripts/build-posts-list.ts (2)

1-5: LGTM! Well-structured interface for table of contents.

The interface is clear and uses appropriate types for its purpose.


29-34: Replace any[] with specific types

The Result interface uses any[] which defeats TypeScript's type safety benefits.

types/scripts/build-rss.ts (1)

9-17: Improve type safety of the RSSItemType

The guid property is typed as any, which defeats TypeScript's type checking benefits.

scripts/finance/index.ts (2)

15-21: Add return type annotation

The async function should have an explicit return type.

 export async function buildFinanceInfoList({
   currentDir,
   configDir,
   financeDir,
   year,
   jsonDataDir
-}: BuildFinanceInfoListProps) {
+}: BuildFinanceInfoListProps): Promise<void> {

34-34: 🛠️ Refactor suggestion

Add file existence checks before operations

The function should verify that source files exist before attempting to write JSON files.

+import { access } from 'fs/promises';
+
+async function checkFileExists(path: string): Promise<boolean> {
+  try {
+    await access(path);
+    return true;
+  } catch {
+    return false;
+  }
+}

 // In the main function
+if (!(await checkFileExists(expensesPath))) {
+  throw new Error(`Source file not found: ${expensesPath}`);
+}
 await writeJSON(expensesPath, expensesJsonPath);

Also applies to: 38-38

types/scripts/tools.ts (1)

1-5: LGTM: Well-structured Links interface with clear documentation

The Links interface is well-documented with clear comments explaining each optional URL's purpose.

scripts/build-meetings.ts (2)

36-39: Good addition of response validation

The explicit check for valid response structure helps prevent runtime errors.


13-13: Add environment variable validation

Similar to the existing review comment about calendar credentials validation.

scripts/tools/categorylist.ts (1)

22-26: Fix incorrect description for Converters category

The description for converters is incorrect as noted in previous review.

scripts/tools/tags-color.ts (1)

154-157: Remove duplicate Scala entry

Scala is already defined in the languagesColor array (lines 51-54). It should not appear in the technologiesColor array.

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

70-72: Consider using npm ci instead of npm install

The change from npm ci to npm install might lead to inconsistent dependency installations across different environments. npm ci is preferred in CI environments as it:

  • Uses package-lock.json exclusively
  • Is more reliable for reproducible builds
  • Removes node_modules before installation
- run: npm install
+ run: npm ci
package.json (1)

6-6: LGTM! TypeScript migration changes look good

The changes consistently implement TypeScript support across the project:

  • ES modules are properly enabled
  • Script commands are updated to use tsx
  • Required TypeScript dependencies and type definitions are added

Also applies to: 11-12, 14-14, 22-25, 27-27, 118-118, 134-135

scripts/build-docs.ts (1)

146-147: Fix loose equality comparisons

Replace loose equality (==) with strict equality (===) for type-safe comparisons.

-if (post?.isRootSection || post?.isSection || index == 0) {
-  if (post?.isRootSection || index == 0) rootSections.push(post.title);
+if (post?.isRootSection || post?.isSection || index === 0) {
+  if (post?.isRootSection || index === 0) rootSections.push(post.title);
🧰 Tools
🪛 eslint

[error] 146-146: Expected '===' and instead saw '=='.

(eqeqeq)


[error] 147-147: Expected '===' and instead saw '=='.

(eqeqeq)

.eslintrc (1)

317-322: LGTM! Rule overrides are well-justified

The override for scripts directory is appropriate:

  • Disabling no-console is necessary for CLI feedback
  • Disabling require-jsdoc is reasonable for scripts
  • Disabling import/no-extraneous-dependencies makes sense for build scripts
utils/languageDetector.ts (1)

3-3: LGTM! Import path updated correctly

The change to import from .cjs extension is appropriate as part of the TypeScript migration.

utils/getStatic.ts (1)

3-3: Verify the existence of next-i18next.config.cjs

The import path has been updated to use the .cjs extension, which is correct for CommonJS modules. However, let's verify that this file exists and the migration is complete.

✅ Verification successful

Import path update to .cjs is correct and verified

The file next-i18next.config.cjs exists and is properly exported using CommonJS module syntax (module.exports), confirming that the import path change in utils/getStatic.ts is correct and aligned with the codebase structure.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the config file exists and verify its module type
if [ -f "next-i18next.config.cjs" ]; then
    echo "File exists. Checking content..."
    # Check if it's properly exported as CommonJS
    rg "module.exports" next-i18next.config.cjs
else
    echo "Warning: next-i18next.config.cjs not found"
    # Check if old file still exists
    [ -f "next-i18next.config.js" ] && echo "Warning: Old config file still exists"
fi

Length of output: 174

Comment on lines +70 to +90
}
} else {
for (const language of toolObject.filters.language) {
const languageSearch = await languageFuse.search(language);

if (languageSearch.length > 0) {
languageArray.push(languageSearch[0].item);
} else {
// adds a new language object in the Fuse list as well as in tool object
// so that it isn't missed out in the UI.
const languageObject = {
name: language as string,
color: 'bg-[#57f281]',
borderColor: 'border-[#37f069]'
};

languageList.push(languageObject);
languageArray.push(languageObject);
languageFuse = new Fuse(languageList, options);
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reinitialize Fuse instances only when necessary.
Each time a new language is added, we recreate the Fuse instance. While this ensures up-to-date searches, it might be an overkill if many tools have distinct languages. Consider batching these additions if performance is a concern.

Comment on lines +9 to +11
export async function buildAdoptersList() {
writeJSON('config/adopters.yml', resolve(currentDirPath, '../../config', 'adopters.json'));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling and improve path resolution.

The function lacks error handling for the writeJSON operation and could benefit from more robust path resolution.

Consider this implementation:

 export async function buildAdoptersList() {
-  writeJSON('config/adopters.yml', resolve(currentDirPath, '../../config', 'adopters.json'));
+  try {
+    const outputPath = 'config/adopters.yml';
+    const inputPath = resolve(currentDirPath, '../../config', 'adopters.json');
+    await writeJSON(outputPath, inputPath);
+    console.log(`Successfully built adopters list at ${outputPath}`);
+  } catch (error) {
+    console.error('Failed to build adopters list:', error);
+    throw 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
export async function buildAdoptersList() {
writeJSON('config/adopters.yml', resolve(currentDirPath, '../../config', 'adopters.json'));
}
export async function buildAdoptersList() {
try {
const outputPath = 'config/adopters.yml';
const inputPath = resolve(currentDirPath, '../../config', 'adopters.json');
await writeJSON(outputPath, inputPath);
console.log(`Successfully built adopters list at ${outputPath}`);
} catch (error) {
console.error('Failed to build adopters list:', error);
throw error;
}
}

Comment on lines +9 to +14
const result = await axios.get('https://api.github.com/search/code?q=filename:.asyncapi-tool', {
headers: {
accept: 'application/vnd.github.text-match+json',
authorization: `token ${process.env.GITHUB_TOKEN}`
}
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add pagination handling for GitHub API

The GitHub Search API is paginated and may return incomplete results. Consider implementing pagination handling to ensure all tools are retrieved.

-    const result = await axios.get('https://api.github.com/search/code?q=filename:.asyncapi-tool', {
+    const results = [];
+    let page = 1;
+    let hasNextPage = true;
+    
+    while (hasNextPage) {
+      const result = await axios.get(
+        `https://api.github.com/search/code?q=filename:.asyncapi-tool&page=${page}`,
+        {
+          headers: {
+            accept: 'application/vnd.github.text-match+json',
+            authorization: `token ${process.env.GITHUB_TOKEN}`
+          }
+        }
+      );
+      
+      results.push(...result.data.items);
+      hasNextPage = result.data.items.length === 30; // GitHub's default page size
+      page++;
+    }
+    
+    return { items: results };

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

json: TocItem[];
content: string;
highest: number;
tokens: any[];
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace 'any[]' with specific type for tokens

The tokens property uses any[] which reduces type safety. Consider defining a specific type for the tokens array.

-    tokens: any[];
+    tokens: Array<{
+      type: string;
+      text: string;
+      depth?: number;
+    }>;
📝 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
tokens: any[];
tokens: Array<{
type: string;
text: string;
depth?: number;
}>;

Comment on lines 1 to 7
export type BlogPostTypes = 'docs' | 'blog' | 'about' | 'docsTree';
export type Enclosure = {
'@url': string;
'@length': number;
'@type': string;
enclosure: Enclosure;
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential infinite recursion in Enclosure type

The Enclosure type includes itself recursively without a termination condition, which could lead to infinite type recursion.

Consider making the recursive property optional:

 export type Enclosure = {
   '@url': string;
   '@length': number;
   '@type': string;
-  enclosure: Enclosure;
+  enclosure?: Enclosure;
 };
📝 Committable suggestion

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

Suggested change
export type BlogPostTypes = 'docs' | 'blog' | 'about' | 'docsTree';
export type Enclosure = {
'@url': string;
'@length': number;
'@type': string;
enclosure: Enclosure;
};
export type BlogPostTypes = 'docs' | 'blog' | 'about' | 'docsTree';
export type Enclosure = {
'@url': string;
'@length': number;
'@type': string;
enclosure?: Enclosure;
};

Comment on lines +39 to +44
interface Filters {
language?: Array<LanguageColorItem | string>;
technology?: Array<LanguageColorItem | string>;
categories: Array<Category | string>; // Categories are used to group tools by different use cases.
hasCommercial?: boolean; // Indicate if your tool is open source or commercial offering.
}
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 stricter typing for Filters interface

The union with string in arrays allows any string value, which could lead to runtime errors.

interface Filters {
-  language?: Array<LanguageColorItem | string>;
-  technology?: Array<LanguageColorItem | string>;
-  categories: Array<Category | string>;
+  language?: Array<LanguageColorItem>;
+  technology?: Array<LanguageColorItem>;
+  categories: Array<Category>;
  hasCommercial?: boolean;
}
📝 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
interface Filters {
language?: Array<LanguageColorItem | string>;
technology?: Array<LanguageColorItem | string>;
categories: Array<Category | string>; // Categories are used to group tools by different use cases.
hasCommercial?: boolean; // Indicate if your tool is open source or commercial offering.
}
interface Filters {
language?: Array<LanguageColorItem>;
technology?: Array<LanguageColorItem>;
categories: Array<Category>;
hasCommercial?: boolean; // Indicate if your tool is open source or commercial offering.
}

Comment on lines 51 to +52
`https://github.com/asyncapi/community/issues/${e.extendedProperties.private.ISSUE_ID}`,
banner:
e.extendedProperties?.private && e.extendedProperties.private.BANNER,
date: new Date(e.start.dateTime),
banner: e.extendedProperties?.private && e.extendedProperties.private.BANNER,
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 optional chaining as suggested by static analysis

The static analysis tool correctly identifies a potential null reference.

-          e.extendedProperties?.private &&
-          `https://github.com/asyncapi/community/issues/${e.extendedProperties.private.ISSUE_ID}`,
+          e.extendedProperties?.private?.ISSUE_ID &&
+          `https://github.com/asyncapi/community/issues/${e.extendedProperties.private.ISSUE_ID}`,

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

🧰 Tools
🪛 Biome (1.9.4)

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines +96 to +100
name: 'AsyncAPI Generator Templates',
tag: 'generator-template',
description:
'The following is a list of templates compatible with AsyncAPI Generator. You can use them to generate apps, clients or documentation from your AsyncAPI documents.'
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure generator-template tag is included in Category type

The 'generator-template' tag used here isn't included in the Category type definition.

Add the tag to the Category type in types/scripts/tools.ts:

type Category =
  | 'api'
  | 'code-first'
  // ... other categories
+ | 'generator-template'
  | 'ide-extension';

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

Comment on lines 144 to +150
fs.writeFile(filePath, frontMatter, { flag: 'wx' }, (err) => {
if (err) {
throw err
throw err;
} else {
console.log(`Blog post generated successfully at ${filePath}`)
console.log(`Blog post generated successfully at ${filePath}`);
}
})
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling for file operations

The current error handling could be more specific. Consider handling different types of errors (e.g., file already exists, permission issues) separately.

-    fs.writeFile(filePath, frontMatter, { flag: 'wx' }, (err) => {
-      if (err) {
-        throw err;
-      } else {
-        console.log(`Blog post generated successfully at ${filePath}`);
-      }
-    });
+    fs.writeFile(filePath, frontMatter, { flag: 'wx' }, (err) => {
+      if (err) {
+        if (err.code === 'EEXIST') {
+          console.error(`File ${filePath} already exists`);
+        } else if (err.code === 'EACCES') {
+          console.error(`Permission denied to create ${filePath}`);
+        } else {
+          console.error(`Error creating file: ${err.message}`);
+        }
+        process.exit(1);
+      }
+      console.log(`Blog post generated successfully at ${filePath}`);
+    });
📝 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.writeFile(filePath, frontMatter, { flag: 'wx' }, (err) => {
if (err) {
throw err
throw err;
} else {
console.log(`Blog post generated successfully at ${filePath}`)
console.log(`Blog post generated successfully at ${filePath}`);
}
})
});
fs.writeFile(filePath, frontMatter, { flag: 'wx' }, (err) => {
if (err) {
if (err.code === 'EEXIST') {
console.error(`File ${filePath} already exists`);
} else if (err.code === 'EACCES') {
console.error(`Permission denied to create ${filePath}`);
} else {
console.error(`Error creating file: ${err.message}`);
}
process.exit(1);
}
console.log(`Blog post generated successfully at ${filePath}`);
});

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 comments (1)
utils/getStatic.ts (1)

Line range hint 44-51: Improve type safety by removing any types

The function uses any types which bypass TypeScript's type checking. Let's improve type safety:

+ import type { GetStaticPropsContext, GetStaticPropsResult } from 'next';
+ 
- export function makeStaticProps(ns = {}) {
+ export function makeStaticProps(ns: string[] = ['common']) {
-   return async function getStaticProps(ctx: any) {
+   return async function getStaticProps(
+     ctx: GetStaticPropsContext<{ lang: string }>
+   ): Promise<GetStaticPropsResult<unknown>> {
     return {
       props: await getI18nProps(ctx, ns)
     };
   };
 }
🧰 Tools
🪛 eslint

[error] 48-48: Delete ,

(prettier/prettier)


[error] 48-48: Unexpected trailing comma.

(comma-dangle)

♻️ Duplicate comments (4)
utils/getStatic.ts (4)

12-13: ⚠️ Potential issue

Remove trailing commas to match ESLint rules

The trailing commas conflict with the project's ESLint configuration.

Apply this fix:

-      lang: lng,
-    },
+      lang: lng
+    }
🧰 Tools
🪛 eslint

[error] 12-12: Delete ,

(prettier/prettier)


[error] 12-12: Unexpected trailing comma.

(comma-dangle)


[error] 13-13: Delete ,

(prettier/prettier)


[error] 13-13: Unexpected trailing comma.

(comma-dangle)


22-22: ⚠️ Potential issue

Remove trailing comma to match ESLint rules

The trailing comma conflicts with the project's ESLint configuration.

Apply this fix:

-  paths: getI18nPaths(),
+  paths: getI18nPaths()
🧰 Tools
🪛 eslint

[error] 22-22: Delete ,

(prettier/prettier)


[error] 22-22: Unexpected trailing comma.

(comma-dangle)


34-34: ⚠️ Potential issue

Remove trailing comma to match ESLint rules

The trailing comma conflicts with the project's ESLint configuration.

Apply this fix:

-    ...(await serverSideTranslations(locale, ns)),
+    ...(await serverSideTranslations(locale, ns))
🧰 Tools
🪛 eslint

[error] 34-34: Delete ,

(prettier/prettier)


[error] 34-34: Unexpected trailing comma.

(comma-dangle)


48-48: ⚠️ Potential issue

Remove trailing comma to match ESLint rules

The trailing comma conflicts with the project's ESLint configuration.

Apply this fix:

-      props: await getI18nProps(ctx, ns as any),
+      props: await getI18nProps(ctx, ns as any)
🧰 Tools
🪛 eslint

[error] 48-48: Delete ,

(prettier/prettier)


[error] 48-48: 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 b96f142 and 5bb3fde.

📒 Files selected for processing (1)
  • utils/getStatic.ts (5 hunks)
🧰 Additional context used
🪛 eslint
utils/getStatic.ts

[error] 12-12: Delete ,

(prettier/prettier)


[error] 12-12: Unexpected trailing comma.

(comma-dangle)


[error] 13-13: Delete ,

(prettier/prettier)


[error] 13-13: Unexpected trailing comma.

(comma-dangle)


[error] 22-22: Delete ,

(prettier/prettier)


[error] 22-22: Unexpected trailing comma.

(comma-dangle)


[error] 34-34: Delete ,

(prettier/prettier)


[error] 34-34: Unexpected trailing comma.

(comma-dangle)


[error] 48-48: Delete ,

(prettier/prettier)


[error] 48-48: Unexpected trailing comma.

(comma-dangle)

🔇 Additional comments (1)
utils/getStatic.ts (1)

3-3: LGTM! Good use of explicit .cjs extension

The change to use .cjs extension explicitly indicates CommonJS module format, which is good practice for clarity in mixed module environments.

@@ -31,7 +31,7 @@ export const getStaticPaths = () => ({
export async function getI18nProps(ctx: any, ns = ['common']) {
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 adding proper TypeScript types for the context parameter

Since we're migrating to TypeScript, we should avoid using any type as it defeats TypeScript's type safety benefits. Consider using Next.js's built-in types.

- export async function getI18nProps(ctx: any, ns = ['common']) {
+ import type { GetStaticPropsContext } from 'next';
+ export async function getI18nProps(
+   ctx: GetStaticPropsContext<{ lang: string }>,
+   ns = ['common']
+ ) {

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

@asyncapi-bot
Copy link
Contributor

asyncapi-bot commented Dec 24, 2024

⚡️ Lighthouse report for the changes in this PR:

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

Lighthouse ran on https://deploy-preview-3423--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: 3

🧹 Nitpick comments (4)
scripts/build-docs.ts (1)

36-39: Gracefully handle missing parent sections.
When item.parent is missing in the tree, the code throws an error. Consider logging available section keys for better troubleshooting context, or handle the case in which the parent is optional to avoid unexpected exceptions.

types/scripts/build-posts-list.ts (1)

33-33: Avoid broad “any” property expansions.
Using [key: string]: any; can undermine type safety. Consider specifying a more precise type or using generics to limit usage.

scripts/build-post-list.ts (2)

74-117: Consider splitting out recursion for clarity and maintainability.
walkDirectories is a large function that uses nested loops and recursive calls. Breaking it into smaller helper functions can simplify debugging and testing, reducing cognitive overhead.


183-185: Finalize writing results outside of production check.
You currently write to posts.json unconditionally at line 189, but consider running this step even if NODE_ENV is not production to simplify local development workflows.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5bb3fde and a08e8fb.

📒 Files selected for processing (4)
  • scripts/build-docs.ts (1 hunks)
  • scripts/build-post-list.ts (1 hunks)
  • types/scripts/build-docs.ts (1 hunks)
  • types/scripts/build-posts-list.ts (1 hunks)
🔇 Additional comments (3)
types/scripts/build-docs.ts (1)

3-7: Good use of typed navigation structures.
The new NavTreeItem, RecursiveChildren, and NavTree types provide clarity on the hierarchical relationships. Keep an eye on maintaining consistency when children can be either RecursiveChildren or an array of Details.

Also applies to: 10-12

types/scripts/build-posts-list.ts (1)

36-41: Ensure references to your Result interface remain consistent.
The Result interface neatly organizes your content arrays and docsTree. If you plan to expand it further, maintain descriptive property names and keep them strongly typed to avoid confusion later on.

scripts/build-post-list.ts (1)

117-117: Avoid infinite recursion with cyclical directories.
When calling walkDirectories recursively, ensure there’s no potential cycle in directory references that could cause a crash or memory blowout.

Comment on lines +100 to +102
if (rootKey === 'reference' && key === 'specification') {
allChildren[key].item.href = childrenOfAllChildren.find((c) => c.isPrerelease === undefined)!.slug;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check for potential undefined value in .find() result.
Using the non-null assertion operator (!) can cause runtime errors if find() returns undefined. Safely handle this with a conditional check or a fallback error, for example:

- allChildren[key].item.href = childrenOfAllChildren.find((c) => c.isPrerelease === undefined)!.slug;
+ const foundItem = childrenOfAllChildren.find((c) => c.isPrerelease === undefined);
+ if (!foundItem) {
+   throw new Error('No suitable specification version found for reference > specification');
+ }
+ allChildren[key].item.href = foundItem.slug;
📝 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 (rootKey === 'reference' && key === 'specification') {
allChildren[key].item.href = childrenOfAllChildren.find((c) => c.isPrerelease === undefined)!.slug;
}
if (rootKey === 'reference' && key === 'specification') {
const foundItem = childrenOfAllChildren.find((c) => c.isPrerelease === undefined);
if (!foundItem) {
throw new Error('No suitable specification version found for reference > specification');
}
allChildren[key].item.href = foundItem.slug;
}

Comment on lines +183 to +194
if (!structuredPosts[index + 1].isRootElement && !structuredPosts[index + 1].isSection) {
nextPage = {
title: structuredPosts[index + 1].title,
href: structuredPosts[index + 1].slug
};
} else {
nextPage = {
title: `${structuredPosts[index + 1].title} - ${structuredPosts[index + 2].title}`,
href: structuredPosts[index + 2].slug
};
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use consistent naming for root detection.
The code references isRootElement in conditions, but the interface does not define this property (only isRootSection). This mismatch may introduce confusion or bugs. Replace isRootElement with isRootSection (or vice versa) for consistency.

- if (!structuredPosts[index + 1].isRootElement && !structuredPosts[index + 1].isSection) {
+ if (!structuredPosts[index + 1].isRootSection && !structuredPosts[index + 1].isSection) {
📝 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 (!structuredPosts[index + 1].isRootElement && !structuredPosts[index + 1].isSection) {
nextPage = {
title: structuredPosts[index + 1].title,
href: structuredPosts[index + 1].slug
};
} else {
nextPage = {
title: `${structuredPosts[index + 1].title} - ${structuredPosts[index + 2].title}`,
href: structuredPosts[index + 2].slug
};
}
if (!structuredPosts[index + 1].isRootSection && !structuredPosts[index + 1].isSection) {
nextPage = {
title: structuredPosts[index + 1].title,
href: structuredPosts[index + 1].slug
};
} else {
nextPage = {
title: `${structuredPosts[index + 1].title} - ${structuredPosts[index + 2].title}`,
href: structuredPosts[index + 2].slug
};
}

Comment on lines +136 to +138
const fileBaseName = basename(data.slug); // ex. v2.0.0 | v2.1.0-next-spec.1
const fileNameOfBaseName = fileBaseName.split('-')[0]; // v2.0.0 | v2.1.0

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify usage of basename with potential undefined slug.
data.slug might be undefined in some edge cases. Use a fallback or guard clause to avoid unexpected errors when calling basename.

- const fileBaseName = basename(data.slug);
+ if (!data.slug) {
+   throw new Error('Missing slug property for file in specification folder.');
+ }
+ const fileBaseName = basename(data.slug);
📝 Committable suggestion

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

Suggested change
const fileBaseName = basename(data.slug); // ex. v2.0.0 | v2.1.0-next-spec.1
const fileNameOfBaseName = fileBaseName.split('-')[0]; // v2.0.0 | v2.1.0
if (!data.slug) {
throw new Error('Missing slug property for file in specification folder.');
}
const fileBaseName = basename(data.slug); // ex. v2.0.0 | v2.1.0-next-spec.1
const fileNameOfBaseName = fileBaseName.split('-')[0]; // v2.0.0 | v2.1.0

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

♻️ Duplicate comments (1)
scripts/build-rss.ts (1)

13-24: 🛠️ Refactor suggestion

Consider using a robust HTML sanitization library.

The current manual string replacements could miss edge cases:

  1. Nested HTML entities
  2. Malformed input
  3. Less common HTML entities

Consider using a dedicated HTML sanitization library like html-entities:

-function clean(s: string) {
-  let cleanS = s;
-
-  cleanS = cleanS.split('&ltspan&gt').join('');
-  cleanS = cleanS.split('&amp').join('&');
-  cleanS = cleanS.split('&#39;').join("'");
-  cleanS = cleanS.split('&lt;').join('<');
-  cleanS = cleanS.split('&gt;').join('>');
-  cleanS = cleanS.split('&quot;').join('"');
-
-  return cleanS;
-}
+import { decode } from 'html-entities';
+
+function clean(s: string) {
+  return decode(s);
+}
🧹 Nitpick comments (2)
scripts/build-rss.ts (2)

48-51: Avoid type assertions with empty objects.

Using type assertions with empty objects can lead to runtime errors.

Consider initializing with required properties:

-const feed = {} as { rss: RSS };
-const rss = {} as RSS;
+const feed = { rss: null as unknown as RSS };
+const rss: RSS = {
+  '@version': '2.0',
+  '@xmlns:atom': 'http://www.w3.org/2005/Atom',
+  channel: null as unknown as RSS['channel']
+};

121-124: Improve error handling specificity.

The current error handling catches all errors and wraps them in a generic message.

Consider handling specific error types:

-  } catch (err) {
-    assert(err instanceof Error);
-    throw new Error(`Failed to generate RSS feed: ${err.message}`);
+  } catch (err) {
+    if (err instanceof TypeError) {
+      throw new Error(`Invalid data structure: ${err.message}`);
+    } else if (err instanceof SyntaxError) {
+      throw new Error(`Invalid JSON format: ${err.message}`);
+    } else {
+      assert(err instanceof Error);
+      throw new Error(`Failed to generate RSS feed: ${err.message}`);
+    }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a08e8fb and 9059384.

📒 Files selected for processing (2)
  • scripts/build-rss.ts (1 hunks)
  • types/scripts/build-rss.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • types/scripts/build-rss.ts
🧰 Additional context used
📓 Learnings (1)
scripts/build-rss.ts (1)
Learnt from: akshatnema
PR: asyncapi/website#3101
File: scripts/build-rss.js:4-6
Timestamp: 2024-11-12T06:59:33.852Z
Learning: Converting `getAllPosts()` to asynchronous in `scripts/build-rss.js` causes the existing build system to fail.
🔇 Additional comments (1)
scripts/build-rss.ts (1)

1-5: LGTM! Clean import organization.

The imports are well-organized, separating runtime dependencies from type imports.

Comment on lines +7 to +11
async function getAllPosts() {
const posts = (await import('../config/posts.json', { assert: { type: 'json' } })).default;

return posts;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential build system incompatibility.

Based on previous learnings from PR #3101, making getAllPosts() asynchronous can cause build system failures. Additionally, directly importing posts at build time may not be the best approach.

Consider refactoring to:

  1. Make the function synchronous
  2. Accept posts as a parameter instead of importing directly
-async function getAllPosts() {
-  const posts = (await import('../config/posts.json', { assert: { type: 'json' } })).default;
-
-  return posts;
-}
+function getAllPosts(posts: any) {
+  return posts;
+}
📝 Committable suggestion

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

Suggested change
async function getAllPosts() {
const posts = (await import('../config/posts.json', { assert: { type: 'json' } })).default;
return posts;
}
function getAllPosts(posts: any) {
return posts;
}


item.enclosure = {
'@url': base + post.cover,
'@length': 15026, // dummy value, anything works
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace dummy enclosure length.

Using a dummy value for the enclosure length could cause RSS reader compatibility issues.

Consider calculating the actual file size:

-'@length': 15026, // dummy value, anything works
+// Add this helper function
+async function getImageSize(url: string): Promise<number> {
+  try {
+    const response = await fetch(url);
+    const contentLength = response.headers.get('content-length');
+    return contentLength ? parseInt(contentLength, 10) : 0;
+  } catch (error) {
+    console.warn(`Failed to get image size for ${url}:`, error);
+    return 0;
+  }
+}

// Update the enclosure assignment
-item.enclosure = {
-  '@url': base + post.cover,
-  '@length': 15026,
-  '@type': mimeType
-};
+const imageUrl = base + post.cover;
+const imageSize = await getImageSize(imageUrl);
+item.enclosure = {
+  '@url': imageUrl,
+  '@length': imageSize,
+  '@type': mimeType
+};

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

@JeelRajodiya
Copy link
Contributor Author

@coderabbitai review

Copy link

coderabbitai bot commented Dec 24, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

3 participants