Repository: remarkablemark/html-react-parser (ts)

Issue:
Resolution error when using TypeScript with `moduleResolution` set to  `node16` or `nodenext`
## Expected Behavior

html-react-parser to resolve correctly when using TypeScript with `moduleResolution` set to  `node16` or `nodenext` (effectively: in pure ESM environment). 

## Actual Behavior

html-react-parser is not resolved correctly:

https://arethetypeswrong.github.io/?p=html-react-parser%405.1.2

This also causes the types to be non-functional in VSCode:

<img width="331" alt="image" src="https://github.com/remarkablemark/html-react-parser/assets/5426427/c691457f-5e82-44e2-adae-7f8649ffb7ec">

## Steps to Reproduce

Create a project using TypeScript with `moduleResolution` set to  `node16` or `nodenext`.
Create a file with the following content:

```ts
import parse from 'html-react-parser';
```

you'll already see, when you hover over `parse` bit, that types are not loaded correctly.

## Reproducible Demo

<!--
Creating a bug demo will help speed up the process of resolving the issue:

* CodeSandbox: https://codesandbox.io/s/940pov1l4w
* JSFiddle: https://jsfiddle.net/remarkablemark/7v86d800/
* Replit: https://replit.com/@remarkablemark/html-react-parser
-->

## Environment

- Version: 5.0.7
- Platform: Node.js
- Browser: not applicable
- OS: macOS Sonoma 14.2.1

## Keywords

Relates to https://github.com/remarkablemark/html-dom-parser/issues/669


Pull request description:
fix(esm): fix exported types
## What is the motivation for this pull request?

fix(esm): fix exported types

Fixes #1305

## What is the current behavior?

1. ESM types not working
2. Using global `JSX` namespace

## What is the new behavior?

1. ESM types working
2. Using `React.JSX`

## Checklist:

- [x] [Conventional Commits](https://www.conventionalcommits.org/)
- [x] Tests
- [ ] Documentation

Patch:
diff --git a/esm/index.d.mts b/esm/index.d.mts
index b0bd7bf..0802072 100644
--- a/esm/index.d.mts
+++ b/esm/index.d.mts
@@ -1,2 +1,6 @@
-export * from '../lib/index';
-export { default } from '../lib/index';
+import HTMLReactParser from '../lib/index.js';
+
+export * from '../lib/index.js';
+
+// @ts-expect-error Property 'default' exists on type
+export default HTMLReactParser.default || HTMLReactParser;
diff --git a/src/dom-to-react.ts b/src/dom-to-react.ts
index 6e28a6b..725e934 100644
--- a/src/dom-to-react.ts
+++ b/src/dom-to-react.ts
@@ -1,4 +1,5 @@
 import { cloneElement, createElement, isValidElement } from 'react';
+import type { JSX } from 'react';
 import type { Element, DOMNode, Text } from 'html-dom-parser';
 
 import attributesToProps from './attributes-to-props';
diff --git a/src/index.ts b/src/index.ts
index 9d79fcf..1ba159f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,7 +7,8 @@ import type { HTMLReactParserOptions } from './types';
 export { Comment, Element, ProcessingInstruction, Text } from 'domhandler';
 export type { DOMNode } from 'html-dom-parser';
 
-export { HTMLReactParserOptions, attributesToProps, domToReact, htmlToDOM };
+export type { HTMLReactParserOptions };
+export { attributesToProps, domToReact, htmlToDOM };
 
 const domParserOptions = { lowerCaseAttributeNames: false } as const;
 
diff --git a/src/types.ts b/src/types.ts
index 4c6c1e8..cfe696b 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,7 +1,7 @@
 import type { DomHandlerOptions } from 'domhandler';
 import type { DOMNode } from 'html-dom-parser';
 import type { ParserOptions } from 'htmlparser2';
-import type { ReactNode } from 'react';
+import type { JSX, ReactNode } from 'react';
 
 export interface HTMLReactParserOptions {
   htmlparser2?: ParserOptions & DomHandlerOptions;


Test patch:
diff --git a/__tests__/dom-to-react.test.tsx b/__tests__/dom-to-react.test.tsx
index 8ec21be..302cb27 100644
--- a/__tests__/dom-to-react.test.tsx
+++ b/__tests__/dom-to-react.test.tsx
@@ -30,7 +30,9 @@ describe('domToReact', () => {
   });
 
   it('converts multiple DOM nodes to React', () => {
-    const reactElements = domToReact(htmlToDOM(html.multiple)) as JSX.Element[];
+    const reactElements = domToReact(
+      htmlToDOM(html.multiple),
+    ) as React.JSX.Element[];
     reactElements.forEach((reactElement, index) => {
       expect(reactElement.key).toBe(String(index));
     });
@@ -83,7 +85,7 @@ describe('domToReact', () => {
   });
 
   it('does not have `children` for void elements', () => {
-    const reactElement = domToReact(htmlToDOM(html.img)) as JSX.Element;
+    const reactElement = domToReact(htmlToDOM(html.img)) as React.JSX.Element;
     expect(reactElement.props.children).toBe(undefined);
   });
 
@@ -97,7 +99,7 @@ describe('domToReact', () => {
   it('skips doctype and comments', () => {
     const reactElements = domToReact(
       htmlToDOM(html.doctype + html.single + html.comment + html.single),
-    ) as JSX.Element[];
+    ) as React.JSX.Element[];
     expect(reactElements).toHaveLength(2);
     expect(reactElements[0].key).toBe('1');
     expect(reactElements[1].key).toBe('3');
@@ -177,7 +179,7 @@ describe('replace option', () => {
     (value) => {
       const reactElement = domToReact(htmlToDOM('<br>'), {
         replace: () => value,
-      }) as JSX.Element;
+      }) as React.JSX.Element;
       expect(reactElement).toEqual(<br />);
     },
   );
@@ -185,7 +187,7 @@ describe('replace option', () => {
   it('does not set key for a single node', () => {
     const reactElement = domToReact(htmlToDOM(html.single), {
       replace: () => <div />,
-    }) as JSX.Element;
+    }) as React.JSX.Element;
     expect(reactElement.key).toBe(null);
   });
 
@@ -213,7 +215,7 @@ describe('replace option', () => {
           }
         },
       },
-    ) as JSX.Element[];
+    ) as React.JSX.Element[];
 
     expect(reactElements[0].key).toBe('0');
     expect(reactElements[1].key).toBe('myKey');
@@ -247,7 +249,7 @@ describe('transform option', () => {
       transform: (reactNode, domNode, index) => {
         return <div key={index}>{reactNode}</div>;
       },
-    }) as JSX.Element;
+    }) as React.JSX.Element;
 
     expect(reactElement.key).toBe('0');
     expect(reactElement.props.children.props.children[0].key).toBe('0');