Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix TypeScript issues #1715

Merged
merged 11 commits into from
Mar 20, 2025
Merged

Fix TypeScript issues #1715

merged 11 commits into from
Mar 20, 2025

Conversation

alexeyr-ci2
Copy link
Collaborator

@alexeyr-ci2 alexeyr-ci2 commented Mar 17, 2025

Summary

Follow-up to #1713: enable type-driven rules in TypeScript-ESLint and fix corresponding issues

Pull Request checklist

  • [ ] Add/update test to cover these changes
  • [ ] Update documentation
  • [ ] Update CHANGELOG file

This change is Reviewable

Summary by CodeRabbit

  • Refactor

    • Enhanced asynchronous processing during component rendering and store hydration, enabling smoother, non-blocking updates.
    • Optimized page lifecycle callbacks to better manage concurrent operations.
    • Improved type safety for various components and functions.
  • Bug Fixes

    • Improved error handling and type-safety measures to reduce potential runtime issues and false-positive alerts.
  • Chores

    • Updated linting rules and configuration settings for improved code reliability.
    • Added new configuration file for TypeScript linting.
    • Refined inline documentation to aid future maintenance.

Copy link
Contributor

coderabbitai bot commented Mar 17, 2025

Walkthrough

This pull request introduces a series of changes across multiple TypeScript files and configuration files. It updates ESLint and TypeScript configurations, refines type safety and public API declarations, and converts several synchronous functions to asynchronous ones. Core rendering, hydration, and page lifecycle methods have been modified to use promises. In addition, error handling, stream processing, and logging have been improved with explicit type annotations and enhanced comments. The changes also include minor fixes in type naming and adjustments to utility functions.

Changes

File(s) Change Summary
eslint.config.ts, tsconfig.eslint.json Updated ESLint rules (added 'no-void' and @typescript-eslint/no-confusing-void-expression, disabled @typescript-eslint/no-unnecessary-condition) and changed TS config to extend strict type checking with a new defaultProject. Added a new TS config for ESLint enabling allowSyntheticDefaultImports.
node_package/.../ClientSideRenderer.ts, node_package/.../ReactOnRails.client.ts, node_package/.../clientStartup.ts, node_package/.../pageLifecycle.ts Converted several methods from synchronous to asynchronous, updated prop and callback type annotations, and adjusted promise handling to improve rendering, hydration, and page lifecycle event processes.
node_package/.../ComponentRegistry.ts, node_package/.../context.ts, node_package/.../isRenderFunction.ts, node_package/.../loadReactClientManifest.ts, node_package/.../reactHydrateOrRender.ts, node_package/.../types/index.ts, node_package/.../turbolinksUtils.ts Revised type declarations and imports by removing redundant casts, enforcing explicit type assertions, and updating public API method signatures. Added documentation and ESLint disable comments where appropriate.
node_package/.../ReactOnRailsRSC.ts, node_package/.../streamServerRenderedReactComponent.ts, node_package/.../transformRSCStreamAndReplayConsoleLogs.ts, node_package/.../buildConsoleReplay.ts Improved error handling with explicit error types, corrected a type name spelling, refined stream parameter types, and inserted ESLint disable comments for clarity during logging and processing operations.

Sequence Diagram(s)

sequenceDiagram
    participant CR as ClientSideRenderer
    participant DR as delegateToRenderer (async)
    participant CRender as ComponentRenderer
    CRender->>DR: Call delegateToRenderer(props)
    DR-->>CRender: Return Promise<boolean>
    Note right of CRender: Await rendering/hydration completion
Loading
sequenceDiagram
    participant Startup as clientStartup
    participant ForceRend as renderOrHydrateForceLoadedComponents
    participant ForceHyd as hydrateForceLoadedStores
    Startup->>ForceRend: Invoke renderOrHydrateForceLoadedComponents (void)
    Startup->>ForceHyd: Invoke hydrateForceLoadedStores (void)
    Note over Startup: Components and stores are processed concurrently
Loading

Suggested reviewers

  • Judahmeek

Poem

I'm a little rabbit, hopping through the code,
With async leaps and changes, lightening the road.
Type safety is my carrot, crisp and neat,
Error handling's my burrow, safe and sweet.
Celebrate these changes with a happy, bouncy beat! 🥕🐇


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b492a0 and f403473.

📒 Files selected for processing (4)
  • node_package/src/ClientSideRenderer.ts (10 hunks)
  • node_package/src/ReactOnRails.client.ts (3 hunks)
  • node_package/src/clientStartup.ts (2 hunks)
  • node_package/src/pageLifecycle.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • node_package/src/pageLifecycle.ts
  • node_package/src/ReactOnRails.client.ts
  • node_package/src/ClientSideRenderer.ts
🧰 Additional context used
🧬 Code Definitions (1)
node_package/src/clientStartup.ts (2)
node_package/src/context.ts (2) (2)
  • context (22-24)
  • Context (16-16)
node_package/src/ClientSideRenderer.ts (2) (2)
  • renderOrHydrateForceLoadedComponents (246-247)
  • hydrateForceLoadedStores (281-282)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: dummy-app-integration-tests (newest)
  • GitHub Check: dummy-app-integration-tests (oldest)
🔇 Additional comments (2)
node_package/src/clientStartup.ts (2)

22-22: Function signature updated to improve concurrency

The function is now synchronous instead of async, which aligns with the decision not to block execution while components are being hydrated. This change supports concurrent hydration but may require updates to any code that was awaiting this function.


37-41: Improved concurrency in hydration process

Good improvement! Using the void operator explicitly indicates that you're intentionally ignoring the promise results while still allowing the asynchronous operations to execute in the background. The added comments clearly explain the rationale for this approach.

This change implements the suggestion from the previous review to "avoid blocking the hydration of non-force loaded components, in case the page is already loaded."

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai 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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

@alexeyr-ci alexeyr-ci force-pushed the alexeyr/upgrade-eslint-ts branch from 1bd2b30 to 7d4331e Compare March 17, 2025 08:38
@@ -11,7 +11,9 @@ import { debugTurbolinks } from './turbolinksUtils';

export async function reactOnRailsPageLoaded() {
debugTurbolinks('reactOnRailsPageLoaded');
await Promise.all([hydrateAllStores(), renderOrHydrateAllComponents()]);
const promise = hydrateAllStores();
renderOrHydrateAllComponents();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

renderOrHydrateAllComponents is not async.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lets update renderOrHydrateComponent and make it await until the component is hydrated
replace

return root;

with

await root.waitUntilRendered();

Then, we can make renderOrHydrateForceLoadedComponents, renderOrHydrateAllComponents and reactOnRailsComponentLoaded functions async function

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

const reactHydrate: HydrateOrRenderType = supportsRootApi
? reactDomClient.hydrateRoot
? reactDomClient!.hydrateRoot
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

TypeScript thinks reactDomClient may not be defined here, even though it definitely is.

@alexeyr-ci2
Copy link
Collaborator Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Mar 17, 2025

✅ 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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
node_package/src/loadReactClientManifest.ts (1)

4-24: TODO comment should be addressed.

The function contains a TODO comment on line 14 indicating it should be converted to async. Since the function is already using async/await syntax and returning promises, this TODO comment appears to be obsolete and can be removed.

- // TODO: convert to async
node_package/src/ReactOnRails.client.ts (1)

28-33: Consider simplified multiline string.
The trailing backslash effectively merges lines, but you might consider a standard multi-line template literal without the extra backslash, if you prefer. Functionality-wise, this looks good.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0bf5546 and 7d4331e.

📒 Files selected for processing (17)
  • eslint.config.ts (2 hunks)
  • node_package/src/CallbackRegistry.ts (3 hunks)
  • node_package/src/ClientSideRenderer.ts (8 hunks)
  • node_package/src/ComponentRegistry.ts (2 hunks)
  • node_package/src/ReactOnRails.client.ts (4 hunks)
  • node_package/src/ReactOnRailsRSC.ts (1 hunks)
  • node_package/src/buildConsoleReplay.ts (1 hunks)
  • node_package/src/clientStartup.ts (2 hunks)
  • node_package/src/context.ts (2 hunks)
  • node_package/src/isRenderFunction.ts (1 hunks)
  • node_package/src/loadReactClientManifest.ts (1 hunks)
  • node_package/src/pageLifecycle.ts (3 hunks)
  • node_package/src/reactHydrateOrRender.ts (1 hunks)
  • node_package/src/streamServerRenderedReactComponent.ts (4 hunks)
  • node_package/src/transformRSCStreamAndReplayConsoleLogs.ts (3 hunks)
  • node_package/src/types/index.ts (3 hunks)
  • tsconfig.eslint.json (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
node_package/src/ReactOnRailsRSC.ts (1)
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2025-03-16T17:09:13.561Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
node_package/src/clientStartup.ts (1)
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-03-16T17:09:13.561Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
node_package/src/ReactOnRails.client.ts (1)
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-03-16T17:09:13.561Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
🪛 Biome (1.9.4)
node_package/src/reactHydrateOrRender.ts

[error] 40-40: Do not depend on the value returned by the function ReactDOM.render().

The returned value is legacy and future versions of React might return that value asynchronously.
Check the React documentation for more information.

(lint/correctness/noRenderReturnValue)

🔇 Additional comments (59)
node_package/src/transformRSCStreamAndReplayConsoleLogs.ts (3)

3-3: Improved type specificity for stream parameter.

The type specification has been improved from ReadableStream to ReadableStream<Uint8Array>, which clearly indicates that the function expects a stream of binary data. This is consistent with how the stream is used throughout the function with the reader and decoder.


21-21: Enhanced type safety with explicit cast.

Adding the as RenderResult type assertion improves type safety by ensuring the parsed JSON conforms to the expected structure. This is better than leaving it as the default any type that JSON.parse returns.


30-30: Improved handling of null/undefined HTML values.

The addition of the null coalescing operator (??) ensures consistent handling when html is null or undefined. However, you should be aware that TextEncoder.encode(undefined) will convert undefined to the string "undefined" before encoding, which might not be the intended behavior.

Consider whether an empty string might be more appropriate:

-controller.enqueue(encoder.encode(html ?? undefined));
+controller.enqueue(encoder.encode(html || ""));

This would convert nullish values to an empty string rather than encoding the string "undefined".

tsconfig.eslint.json (1)

1-6: Good addition of a dedicated ESLint TypeScript configuration file.

This configuration file extends the base TypeScript configuration and enables allowSyntheticDefaultImports, which allows importing from modules without default exports as if they had them. This is a good practice that helps maintain compatibility with different module systems and improves code maintainability.

This file will be specifically used by ESLint when checking TypeScript files, ensuring proper type checking during linting.

node_package/src/isRenderFunction.ts (1)

15-15: Appropriate ESLint directive for type checking limitation.

The added ESLint directive properly disables the rule for unsafe member access. This is necessary because the function is checking for prototype properties that may not exist on all possible types, which is a pattern that TypeScript's static analysis can't always correctly validate, but is safe in this context due to the following conditional checks.

node_package/src/ReactOnRailsRSC.ts (1)

49-49: Good type safety improvement with explicit unknown error type.

Changing the error parameter from implicit any to explicit unknown is a TypeScript best practice. This forces code to properly handle errors of unknown type, making the error handling more robust. The subsequent convertToError(e) correctly handles this unknown value.

node_package/src/buildConsoleReplay.ts (1)

40-41: Well-documented ESLint directive in error handling code.

The added ESLint directive with explanatory comment is appropriate for this error handling context. Disabling the base-to-string rule makes sense here since this is already in a catch block where JSON.stringify has failed, and having any string representation of the argument is better than throwing another error.

node_package/src/context.ts (2)

21-21: ESLint rule suppression is appropriate.

The ESLint comment is necessary to suppress the @typescript-eslint/no-invalid-void-type rule which warns against using void as a return type in the function signature. This is a valid use case as the function may return void in some scenarios.


57-57: Type assertion improves type safety.

Adding the explicit type assertion as RailsContext ensures that the parsed JSON conforms to the expected RailsContext interface. This improves type safety and makes the code's intention clearer.

node_package/src/streamServerRenderedReactComponent.ts (4)

20-20: Fixed type name spelling.

The type name has been corrected from BufferdEvent to BufferedEvent, fixing the typo in the original type declaration.


41-41: Updated type reference for consistency.

The variable declaration has been updated to use the corrected type name BufferedEvent.


61-61: Updated type reference for consistency.

The parameter type annotation has been updated to use the corrected type name BufferedEvent.


99-100: Improved type safety with explicit casting.

The ESLint disable comments are appropriate for addressing TypeScript's strict type checking on the toString() method call. The explicit type assertion as string makes it clear that the chunk is being converted to a string type.

node_package/src/reactHydrateOrRender.ts (7)

10-10: Improved type specificity.

Replacing the any type with a more specific typeof import('react-dom/client') improves type safety by providing better type information for the reactDomClient variable.


17-17: Added explicit type assertion.

The type assertion ensures that the required module is correctly typed as typeof import('react-dom/client'), improving type safety.


21-21: Added explicit type assertion for fallback.

The double type assertion (as unknown as) is necessary because TypeScript doesn't allow direct casting between incompatible types. This approach first erases the type with unknown and then assigns the target type.


25-27: Updated ESLint disable comments.

The ESLint disable comments have been updated to include TypeScript-specific rules, making the intent clearer and helping maintain code quality while supporting both React 16 and newer versions.


29-29: Added non-null assertion.

The non-null assertion operator (!) is necessary here because TypeScript cannot statically verify that reactDomClient is defined at this point, even though we've ensured it through the conditional logic.


34-34: Added non-null assertion.

Similar to line 29, the non-null assertion is required to inform TypeScript that reactDomClient is definitely defined at this point in the code execution.


42-42: Updated ESLint enable comments.

The ESLint enable comments have been updated to match the disable comments, ensuring proper rule enforcement.

node_package/src/loadReactClientManifest.ts (6)

4-4: Added type alias for better readability.

Creating a ClientManifest type alias improves code readability and makes it easier to understand the purpose of the data structure.


5-5: Updated map type with new type alias.

Using the newly created ClientManifest type for the map improves consistency and readability.


12-13: Improved variable handling for manifest retrieval.

Storing the result of get() in a variable and checking if it's falsy is more efficient than checking if the map has the key and then retrieving it again.


16-16: Added type assertion for parsed JSON.

The explicit type assertion as ClientManifest ensures that the parsed JSON conforms to the expected structure, improving type safety.


18-18: Direct return of manifest after loading.

Returning the manifest directly after loading it is more efficient than retrieving it again from the map.


24-24: Direct return of cached manifest.

Using the already retrieved manifest variable is more efficient than accessing the map again.

eslint.config.ts (6)

83-88: Good addition of the 'no-void' rule with proper configuration

This rule correctly prevents misuse of the void operator while still allowing it as a statement through the allowAsStatement: true option. This aligns with the PR objective to enable stricter TypeScript rules.


143-143: Excellent upgrade to strict type checking

Switching from tsEslint.configs.recommended to tsEslint.configs.strictTypeChecked will significantly improve type safety across the codebase by enabling stricter type checking rules. This is a key improvement for TypeScript code quality.


149-152: Good solution for import compatibility issues

Adding a defaultProject configuration resolves issues with import syntax compatibility. The explanatory comment clearly documents the reason for this change, which helps future maintainers understand the rationale.


159-164: Appropriate configuration for void expressions

The @typescript-eslint/no-confusing-void-expression rule with ignoreArrowShorthand: true will help maintain clearer code while still allowing practical arrow function shorthand syntax where appropriate.


165-166: Good decision to disable problematic rule

Disabling @typescript-eslint/no-unnecessary-condition due to false positives is a pragmatic choice. The comment explaining why it's disabled provides important context for future maintainers.


173-173: Appropriate template expression configuration

Turning off @typescript-eslint/restrict-template-expressions prevents overly restrictive template string usage while still benefiting from other type safety improvements in the configuration.

node_package/src/ComponentRegistry.ts (2)

1-1: Simplified import statement by removing unused type

Removing the RenderFunction type from imports keeps the code cleaner by eliminating unused imports. This aligns with best practices for module imports.


23-23: Improved type safety by removing unnecessary type casting

The change from (component as RenderFunction).length === 3 to component.length === 3 removes an unnecessary type cast, allowing TypeScript to properly infer the type. This improves type safety and makes the code cleaner.

node_package/src/types/index.ts (3)

10-12: Enhanced Store type definition with proper structure

Replacing the generic unknown type with a specific structure that includes a getState() method significantly improves type safety by more accurately describing the expected shape of store objects.


169-169: Improved ESLint rule comment with specific justification

The enhanced comment for disabling the ESLint rule provides clear context about why this exception is necessary, noting it's inherited from earlier React versions and can't be avoided in this specific case.


184-184: Updated method signature to support asynchronous operation

Changing reactOnRailsStoreLoaded to return Promise<void> instead of void properly supports the asynchronous nature of this operation. This change will ensure correct sequencing of operations during store loading.

node_package/src/CallbackRegistry.ts (4)

32-32: Properly converted method to async for better control flow

Adding the async modifier to initializeTimeoutEvents() enables proper asynchronous execution, allowing this method to correctly wait for page lifecycle events.


49-54: Improved page loading lifecycle handling with async/await

Using await with the onPageLoaded callback ensures that the timeout setup completes properly before continuing execution, improving the reliability of component registry timeout handling.


56-60: Enhanced page unloading lifecycle with proper async handling

Adding await to the onPageUnloaded callback ensures cleanup operations complete correctly during page transitions, preventing potential race conditions or memory leaks.


99-100: Properly awaiting timeout initialization in getOrWaitForItem

Adding await to the initializeTimeoutEvents() call ensures that timeout monitoring is properly initialized before attempting to retrieve or wait for an item, preventing potential race conditions.

node_package/src/clientStartup.ts (3)

14-16: Confirm concurrency approach with store hydration and rendering.
You're calling renderOrHydrateAllComponents() before awaiting store hydration. If the store is required for correct rendering, consider waiting for hydrateAllStores() to complete first. Otherwise, if concurrency is intentional, this is fine.


41-41: Great job ensuring store hydration is awaited.
Awaiting hydrateForceLoadedStores() improves predictability of the initialization process.


44-45: Sequential page lifecycle events.
Awaiting onPageLoaded and onPageUnloaded ensures callbacks finish before continuing. This change looks good.

node_package/src/ReactOnRails.client.ts (3)

155-156: Verify no existing usage depends on synchronous store loading.
Changing reactOnRailsStoreLoaded to async means code calling it won't get immediate store hydration. Ensure no consumers rely on the old synchronous behavior.


206-206: Removal of return statement.
Removing the returned value from setStore appears benign.


346-346: Potential unhandled rejections.
void ClientStartup.clientStartup(ctx); ignores the returned promise. If clientStartup fails, it may go unhandled. Confirm that this is acceptable and doesn't result in silent failures.

node_package/src/pageLifecycle.ts (3)

9-9: Allowing async callbacks.
Expanding PageLifecycleCallback to handle promises is beneficial for asynchronous logic.


19-21: Ignoring callback promises.
Calling void callback(); avoids waiting for potential asynchronous operations, leading to possible unhandled rejections if the callback throws. Confirm if ignoring errors is intentional.

Also applies to: 26-28


76-79: Awaiting callbacks.
Awaiting the callbacks here ensures full completion of asynchronous logic when the page is already loaded or unloaded.

Also applies to: 84-87

node_package/src/ClientSideRenderer.ts (9)

2-2: Clear ESLint directive with explanation

Good practice adding the ESLint comment with a clear explanation for why these rules are disabled (supporting React 16). This makes it easier for future maintainers to understand the rationale and when this can potentially be removed.


17-23: Proper async function signature with improved type safety

Converting delegateToRenderer to an async function is a significant improvement. Changing the props type from Record<string, string> to Record<string, unknown> provides better type flexibility for handling JSON data that may contain various types, not just strings.


35-35: Fixed missing await for renderer function

Adding the await keyword here properly ensures the renderer function completes before continuing. This was missing in the previous code, as noted in a prior comment.


84-84: Improved type safety for parsed JSON

Good change to specify Record<string, unknown> for the parsed JSON content. This more accurately represents JSON data which can contain different types, not just strings.


95-95: Added necessary await for delegateToRenderer

Properly awaiting the now-async delegateToRenderer function is required to ensure the rendering process completes before proceeding with the rest of the function.


166-168: Safer handling of the render promise

The conditional now properly checks for the existence of renderPromise before returning it, which is safer than using non-null assertions. This prevents potential runtime errors if renderPromise is undefined.


186-189: Improved type safety and null handling for store props

The reformatted code now properly handles the case when textContent is null and correctly types the parsed JSON as Record<string, unknown>, which is more appropriate for JSON data with varied types.


197-197: Updated hydrate method signature for consistency

Updating the hydrate method signature to use Record<string, unknown> maintains consistency with the other type changes in this file and better represents the possible types that can be present in props.


210-211: Safer handling of the hydrate promise

Similar to the render promise change, this improvement properly checks for the existence of hydratePromise before returning it, avoiding potential runtime errors if the promise is undefined.

Comment on lines 72 to 86
export function onPageLoaded(callback: PageLifecycleCallback): void {
export async function onPageLoaded(callback: PageLifecycleCallback): Promise<void> {
if (currentPageState === 'load') {
callback();
await callback();
}
pageLoadedCallbacks.add(callback);
initializePageEventListeners();
}

export function onPageUnloaded(callback: PageLifecycleCallback): void {
export async function onPageUnloaded(callback: PageLifecycleCallback): Promise<void> {
if (currentPageState === 'unload') {
callback();
await callback();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@AbanoubGhadban Or do you think these callbacks shouldn't be awaited?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This callback is conditionally awaited only if the page has been unloaded. As a result, the caller of the onPageUnloaded function may mistakenly assume that the callback is awaited, when in fact it is not executed.

@alexeyr-ci alexeyr-ci force-pushed the alexeyr/upgrade-eslint-ts branch from 7d4331e to c5215f1 Compare March 17, 2025 09:26
@Judahmeek Judahmeek force-pushed the alexeyr/upgrade-eslint branch from 0bf5546 to f164ef7 Compare March 18, 2025 04:17
Base automatically changed from alexeyr/upgrade-eslint to master March 18, 2025 04:24
@alexeyr-ci alexeyr-ci force-pushed the alexeyr/upgrade-eslint-ts branch 2 times, most recently from c744e93 to c1e861e Compare March 18, 2025 06:31
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
node_package/src/reactHydrateOrRender.ts (1)

17-17: Type assertions properly handle React DOM client implementation.

The type assertions ensure TypeScript correctly understands the types in different code paths. The non-null assertions (!) on lines 29 and 34 address the issue mentioned in your previous comment about TypeScript thinking reactDomClient may not be defined.

While the implementation is correct, consider if there's a way to refactor this to avoid non-null assertions in the future, as they bypass TypeScript's null checking.

Also applies to: 21-21, 29-29, 34-34

eslint.config.ts (1)

173-173: Consider adding a comment explaining the rationale for disabling this rule.

The @typescript-eslint/restrict-template-expressions rule has been disabled, but there's no comment explaining why. Adding a brief comment explaining the rationale (similar to the comment for no-unnecessary-condition) would improve code maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d4331e and c1e861e.

📒 Files selected for processing (18)
  • eslint.config.ts (2 hunks)
  • node_package/src/CallbackRegistry.ts (3 hunks)
  • node_package/src/ClientSideRenderer.ts (9 hunks)
  • node_package/src/ComponentRegistry.ts (2 hunks)
  • node_package/src/ReactOnRails.client.ts (4 hunks)
  • node_package/src/ReactOnRailsRSC.ts (1 hunks)
  • node_package/src/buildConsoleReplay.ts (1 hunks)
  • node_package/src/clientStartup.ts (2 hunks)
  • node_package/src/context.ts (2 hunks)
  • node_package/src/isRenderFunction.ts (1 hunks)
  • node_package/src/loadReactClientManifest.ts (1 hunks)
  • node_package/src/pageLifecycle.ts (3 hunks)
  • node_package/src/reactHydrateOrRender.ts (1 hunks)
  • node_package/src/streamServerRenderedReactComponent.ts (4 hunks)
  • node_package/src/transformRSCStreamAndReplayConsoleLogs.ts (3 hunks)
  • node_package/src/turbolinksUtils.ts (1 hunks)
  • node_package/src/types/index.ts (3 hunks)
  • tsconfig.eslint.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (14)
  • tsconfig.eslint.json
  • node_package/src/ReactOnRailsRSC.ts
  • node_package/src/isRenderFunction.ts
  • node_package/src/buildConsoleReplay.ts
  • node_package/src/types/index.ts
  • node_package/src/streamServerRenderedReactComponent.ts
  • node_package/src/ComponentRegistry.ts
  • node_package/src/clientStartup.ts
  • node_package/src/loadReactClientManifest.ts
  • node_package/src/transformRSCStreamAndReplayConsoleLogs.ts
  • node_package/src/CallbackRegistry.ts
  • node_package/src/ClientSideRenderer.ts
  • node_package/src/ReactOnRails.client.ts
  • node_package/src/context.ts
🧰 Additional context used
🪛 Biome (1.9.4)
node_package/src/reactHydrateOrRender.ts

[error] 40-40: Do not depend on the value returned by the function ReactDOM.render().

The returned value is legacy and future versions of React might return that value asynchronously.
Check the React documentation for more information.

(lint/correctness/noRenderReturnValue)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: dummy-app-integration-tests (oldest)
  • GitHub Check: dummy-app-integration-tests (newest)
🔇 Additional comments (14)
node_package/src/turbolinksUtils.ts (1)

11-16: Good improvement to type safety and documentation.

This change enhances the debugTurbolinks function in two valuable ways:

  1. Updating the parameter type from string[] to unknown[] correctly matches how console.log works, allowing developers to log any type of values, not just strings.
  2. Adding JSDoc comments improves code clarity by documenting the function's purpose and behavior.

This change aligns with TypeScript best practices and makes the code more maintainable.

node_package/src/reactHydrateOrRender.ts (3)

10-10: Improved type safety with specific type declaration.

The change from any to typeof import('react-dom/client') provides much better type safety and self-documentation. This is a good practice that helps TypeScript understand the expected structure of the variable.


25-27: Updated ESLint directives to include TypeScript-specific rules.

The ESLint disable/enable comments have been appropriately updated to include TypeScript-specific rules, ensuring proper linting behavior.

Also applies to: 42-42


39-40: Be cautious with the return value from ReactDOM.render().

Static analysis has flagged that depending on the return value of ReactDOM.render() may cause issues in future React versions, as this value might be returned asynchronously.

Since this code is handling the differences between React 16+ and React 18+, this is likely an acceptable tradeoff for backward compatibility, but worth noting for future refactoring.

🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Do not depend on the value returned by the function ReactDOM.render().

The returned value is legacy and future versions of React might return that value asynchronously.
Check the React documentation for more information.

(lint/correctness/noRenderReturnValue)

eslint.config.ts (5)

83-88: Improved void operator handling with appropriate allowances.

The 'no-void' rule with 'allowAsStatement: true' is a good addition that prevents misuse of void in expressions while still allowing its legitimate use as a statement (e.g., for explicitly ignoring promises).


143-143: Significant enhancement to type checking with strictTypeChecked.

Upgrading from tsEslint.configs.recommended to tsEslint.configs.strictTypeChecked is a major improvement. This enables much more thorough type checking and will help catch type-related issues earlier in the development process. This change is perfectly aligned with the PR objective of enabling type-driven rules.


149-152: Added default project configuration to solve import issues.

The addition of the defaultProject setting with a clear comment explaining its purpose is a good solution to the specific import syntax issues mentioned.


159-164: Added rule to prevent confusing void expressions with sensible exceptions.

The @typescript-eslint/no-confusing-void-expression rule with ignoreArrowShorthand: true is a good addition that enforces clear code without being too restrictive. It prevents potentially confusing expressions that discard their value, while still allowing this pattern in arrow function shorthand (e.g., () => void fn()).


165-166: Disabled rule with too many false positives.

The decision to disable @typescript-eslint/no-unnecessary-condition with a clear comment explaining that it produces too many false positives is reasonable. This prevents developer frustration with incorrect warnings.

node_package/src/pageLifecycle.ts (5)

9-9: Good enhancement to support async callbacks.

The type signature change allows callbacks to be either synchronous or asynchronous, improving type safety and flexibility in the API.


26-28: Verify async callback handling in event listeners.

Same issue as with runPageLoadedCallbacks - the void operator ignores Promise returns, creating an inconsistency with how callbacks are handled in onPageUnloaded.


76-76: Good implementation of async callback handling.

Making onPageLoaded an async function and awaiting the callback ensures that any asynchronous operations in the callback complete before proceeding. This improves the sequential execution of operations during page loading.

Note that this differs from how callbacks are handled in runPageLoadedCallbacks (which uses void callback() instead of awaiting).

Also applies to: 78-78


84-84: Good implementation of async callback handling.

Similar to onPageLoaded, making onPageUnloaded async and awaiting the callback ensures proper sequencing of asynchronous operations during page unloading.

Note that this differs from how callbacks are handled in runPageUnloadedCallbacks (which uses void callback() instead of awaiting).

Also applies to: 86-86


19-21:

❓ Verification inconclusive

Verify async callback handling in event listeners.

The void operator is used here to explicitly ignore the Promise returned by potentially async callbacks. However, this means that event listeners won't wait for async callbacks to complete, which differs from how they're handled in onPageLoaded and onPageUnloaded.

Is this inconsistency intentional? If all callbacks should be awaited, consider updating the event handlers to be async functions that await all callbacks sequentially or in parallel using Promise.all().


🏁 Script executed:

#!/bin/bash
# Check if there are any direct calls to these event handlers that might expect them to be synchronous
rg -A 2 -B 2 "runPageLoadedCallbacks|runPageUnloadedCallbacks" --type ts

Length of output: 2652


Clarification on async callback behavior in event listeners

The event listeners currently invoke callbacks using the void operator (e.g. void callback()), which discards any returned Promise. This differs from the handling of async callbacks in other parts of the code (such as in onPageLoaded/onPageUnloaded), where awaiting might be expected. Can you confirm whether the fire-and-forget approach here is intentional? If awaiting async callbacks is desired, please consider refactoring these handlers into async functions that either await callbacks sequentially or use Promise.all() for parallel execution.

@alexeyr-ci alexeyr-ci force-pushed the alexeyr/upgrade-eslint-ts branch from c1e861e to f2c83de Compare March 19, 2025 08:09
Copy link
Collaborator

@AbanoubGhadban AbanoubGhadban left a comment

Choose a reason for hiding this comment

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

Good work, only needs some small changes

@@ -92,7 +92,7 @@ class ComponentRenderer {
return;
}

if (delegateToRenderer(componentObj, props, railsContext, domNodeId, trace)) {
if (await delegateToRenderer(componentObj, props, railsContext, domNodeId, trace)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (await delegateToRenderer(componentObj, props, railsContext, domNodeId, trace)) {
// Abort if component unmounted during awaiting the async operation
if (await delegateToRenderer(componentObj, props, railsContext, domNodeId, trace) || this.state === 'unmounted') {

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Reverted.

Comment on lines 72 to 86
export function onPageLoaded(callback: PageLifecycleCallback): void {
export async function onPageLoaded(callback: PageLifecycleCallback): Promise<void> {
if (currentPageState === 'load') {
callback();
await callback();
}
pageLoadedCallbacks.add(callback);
initializePageEventListeners();
}

export function onPageUnloaded(callback: PageLifecycleCallback): void {
export async function onPageUnloaded(callback: PageLifecycleCallback): Promise<void> {
if (currentPageState === 'unload') {
callback();
await callback();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This callback is conditionally awaited only if the page has been unloaded. As a result, the caller of the onPageUnloaded function may mistakenly assume that the callback is awaited, when in fact it is not executed.

@@ -36,9 +38,9 @@ export async function clientStartup(context: Context): Promise<void> {

// force loaded components and stores are rendered and hydrated immediately
renderOrHydrateForceLoadedComponents();
hydrateForceLoadedStores();
await hydrateForceLoadedStores();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do not wait here. The hydration process can handle the concurrent hydration of components and stores. Therefore, let's avoid blocking the hydration of non-force loaded components, in case the page is already loaded.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(and 48d03d1).

@@ -11,7 +11,9 @@ import { debugTurbolinks } from './turbolinksUtils';

export async function reactOnRailsPageLoaded() {
debugTurbolinks('reactOnRailsPageLoaded');
await Promise.all([hydrateAllStores(), renderOrHydrateAllComponents()]);
const promise = hydrateAllStores();
renderOrHydrateAllComponents();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Lets update renderOrHydrateComponent and make it await until the component is hydrated
replace

return root;

with

await root.waitUntilRendered();

Then, we can make renderOrHydrateForceLoadedComponents, renderOrHydrateAllComponents and reactOnRailsComponentLoaded functions async function

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2c83de and dab740c.

📒 Files selected for processing (1)
  • node_package/src/ClientSideRenderer.ts (9 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
node_package/src/ClientSideRenderer.ts (2)
node_package/src/types/index.ts (3) (3)
  • RegisteredComponent (101-114)
  • RailsContext (17-35)
  • RenderFunction (93-93)
node_package/src/turbolinksUtils.ts (1) (1)
  • debugTurbolinks (16-25)
🪛 ESLint
node_package/src/ClientSideRenderer.ts

[error] 96-96: Replace await·delegateToRenderer(componentObj,·props,·railsContext,·domNodeId,·trace)·||·this.state·===·'unmounted' with ⏎··········(await·delegateToRenderer(componentObj,·props,·railsContext,·domNodeId,·trace))·||⏎··········this.state·===·'unmounted'⏎········

(prettier/prettier)

🪛 GitHub Check: examples (newest)
node_package/src/ClientSideRenderer.ts

[failure] 96-96:
This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Check: examples (oldest)
node_package/src/ClientSideRenderer.ts

[failure] 96-96:
This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Check: build
node_package/src/ClientSideRenderer.ts

[failure] 96-96:
This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Check: build-dummy-app-webpack-test-bundles (oldest)
node_package/src/ClientSideRenderer.ts

[failure] 96-96:
This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Check: build-dummy-app-webpack-test-bundles (newest)
node_package/src/ClientSideRenderer.ts

[failure] 96-96:
This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Actions: Lint JS and Ruby
node_package/src/ClientSideRenderer.ts

[error] 96-96: error TS2367: This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Actions: JS unit tests for Renderer package
node_package/src/ClientSideRenderer.ts

[error] 96-96: TS2367: This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.


[error] 96-96: TS2367: This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Actions: Main test
node_package/src/ClientSideRenderer.ts

[error] 96-96: error TS2367: This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🪛 GitHub Actions: Generator tests
node_package/src/ClientSideRenderer.ts

[error] 96-96: error TS2367: This comparison appears to be unintentional because the types '"rendering" | "rendered"' and '"unmounted"' have no overlap.

🔇 Additional comments (8)
node_package/src/ClientSideRenderer.ts (8)

17-23: Improved type safety by making delegateToRenderer asynchronous

The function is now properly defined as async with Promise return type, which allows proper handling of asynchronous renderer functions. This change aligns with the component lifecycle and prevents potential race conditions.


35-35: Properly awaiting the renderer function

The renderer function is now correctly awaited, ensuring proper sequencing of operations. This fixes the issue mentioned in previous reviews where the renderer wasn't properly awaited.


84-84: Enhanced type safety with proper JSON parsing type cast

Using Record<string, unknown> instead of implicitly assuming all props are strings provides better type safety and accurately represents the variety of prop types that can be passed.


167-169: Improved null safety check for render promise

The condition now correctly checks for the existence of this.renderPromise before returning it, preventing potential null reference errors.


188-190: Enhanced type safety for store props

Using Record<string, unknown> for store props ensures proper type handling for various data types that can be stored, not just strings.


198-198: Updated method signature for better type safety

The hydrate method now accepts Record<string, unknown> for props, which is consistent with the other changes and provides better type safety.


211-213: Improved null safety check for hydrate promise

The condition now correctly checks for the existence of this.hydratePromise before returning it, preventing potential null reference errors.


226-226: Added debugging for Turbolinks rendering

The debug statement helps with troubleshooting Turbolinks-related rendering issues, which can be challenging to diagnose otherwise.

@alexeyr-ci alexeyr-ci force-pushed the alexeyr/upgrade-eslint-ts branch from dab740c to e308754 Compare March 19, 2025 17:04
@alexeyr-ci alexeyr-ci force-pushed the alexeyr/upgrade-eslint-ts branch from e308754 to 48d03d1 Compare March 19, 2025 17:15
@alexeyr-ci2
Copy link
Collaborator Author

@AbanoubGhadban

Lets update renderOrHydrateComponent and make it await until the component is hydrated
replace

Since I can't reply anymore, fixed version of this is 48d03d1.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
node_package/src/pageLifecycle.ts (1)

76-76: Removing explicit return type is acceptable but consider adding it back.

While TypeScript can infer the return type, explicitly declaring it as void in public API functions improves code readability and provides clearer documentation.

-export function onPageLoaded(callback: PageLifecycleCallback) {
+export function onPageLoaded(callback: PageLifecycleCallback): void {
-export function onPageUnloaded(callback: PageLifecycleCallback) {
+export function onPageUnloaded(callback: PageLifecycleCallback): void {

Also applies to: 84-84

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48d03d1 and 9b492a0.

📒 Files selected for processing (4)
  • node_package/src/ClientSideRenderer.ts (10 hunks)
  • node_package/src/ReactOnRails.client.ts (3 hunks)
  • node_package/src/clientStartup.ts (2 hunks)
  • node_package/src/pageLifecycle.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • node_package/src/ReactOnRails.client.ts
  • node_package/src/ClientSideRenderer.ts
🧰 Additional context used
🧬 Code Definitions (1)
node_package/src/clientStartup.ts (2)
node_package/src/context.ts (2) (2)
  • context (22-24)
  • Context (16-16)
node_package/src/ClientSideRenderer.ts (2) (2)
  • renderOrHydrateForceLoadedComponents (246-247)
  • hydrateForceLoadedStores (281-282)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: dummy-app-integration-tests (oldest)
  • GitHub Check: dummy-app-integration-tests (newest)
🔇 Additional comments (7)
node_package/src/clientStartup.ts (3)

22-22: Function signature updated to reflect non-async behavior.

The function has been changed from async to a regular function without a return type. This aligns with the intentional design not to await force-loaded components and stores hydration, allowing for concurrent processing.


37-39: Good documentation of design decision.

The comments clearly explain the rationale behind not awaiting the hydration process, which helps future developers understand the concurrent hydration approach.


40-41: Appropriate use of void operator.

Using the void operator explicitly discards the Promise returns from these async functions, making it clear that we're intentionally not awaiting their completion. This matches the design decision explained in the comments and aligns with the previous review discussions about avoiding blocking the hydration of non-force loaded components.

node_package/src/pageLifecycle.ts (4)

9-9: The type signature change improves flexibility.

The updated PageLifecycleCallback type now supports both synchronous and asynchronous callbacks, which makes the API more flexible and aligns with modern TypeScript practices.


19-21: Consider awaiting promises returned by callbacks.

Adding void before callback() explicitly ignores any promises returned by callbacks. This might lead to unexpected behavior if callers expect their asynchronous callbacks to complete before continuing execution.

This approach ensures callbacks don't block execution, but might lead to race conditions if callers expect their callbacks to be awaited. Consider whether Promise-returning callbacks should be properly awaited instead.

-  pageLoadedCallbacks.forEach((callback) => {
-    void callback();
-  });
+  // Run callbacks sequentially, awaiting any promises
+  const runCallbacks = async () => {
+    for (const callback of pageLoadedCallbacks) {
+      await callback();
+    }
+  };
+  void runCallbacks();

26-28: Consider awaiting promises returned by callbacks.

Similar to the issue in runPageLoadedCallbacks, using void callback() ignores any promises returned by callbacks in the unload cycle.


78-78: Address potential issue with conditionally awaited callbacks.

As noted in a previous review, these callbacks are conditionally executed but never awaited, which may lead to confusion for callers expecting the promises to be resolved before the function returns.

This relates to the previous comment from @AbanoubGhadban: "This callback is conditionally awaited only if the page has been unloaded. As a result, the caller of the onPageUnloaded function may mistakenly assume that the callback is awaited, when in fact it is not executed."

Consider documenting this behavior or refactoring to ensure consistent promise handling.

Also applies to: 86-86

Copy link
Collaborator

@AbanoubGhadban AbanoubGhadban left a comment

Choose a reason for hiding this comment

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

LGTM

@alexeyr-ci alexeyr-ci force-pushed the alexeyr/upgrade-eslint-ts branch from 9b492a0 to f403473 Compare March 20, 2025 08:12
@alexeyr-ci2 alexeyr-ci2 merged commit 1fc6ce0 into master Mar 20, 2025
11 checks passed
@alexeyr-ci2 alexeyr-ci2 deleted the alexeyr/upgrade-eslint-ts branch March 20, 2025 08:28
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