Skip to content

Commit

Permalink
feat: New <react. preprocessing
Browse files Browse the repository at this point in the history
- global types for sveltify & react, these values are imported or generated when used but not defined.
- Compatible with `<react:*` notation, but a deprecation warning
- PreProcessor injects react dependencies as second argument to sveltify.
- Added migration docs

Fixes #1 & #32
  • Loading branch information
bfanger committed Oct 19, 2024
1 parent ce057e1 commit 2d7f6a3
Show file tree
Hide file tree
Showing 32 changed files with 326 additions and 260 deletions.
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,40 @@ After you've gradually converted all components to Svelte you can remove this pr

> Embrace
Inside the Svelte template prepend the name of the component with `react:` prefix.
Inside the Svelte template prepend the name of the component with `react.` prefix.

Instead of `<Button>`, you'd write `<react:Button>`
Instead of `<Button>`, you'd write `<react.Button>`

You're also able to use libraries from the react ecosystem, react-youtube for example:

```svelte
<script>
import YouTube from "react-youtube";
const react = sveltify({ YouTube }); // Optional, but improves tooling
</script>
<react:YouTube videoId="AdNJ3fydeao" />
<react.YouTube videoId="AdNJ3fydeao" />
```

The snippet above would be generate:

```svelte
<!-- Generated by svelte-preprocess-react -->
<script>
import { sveltify } from "svelte-preprocess-react";
import { createPortal } from "react-dom";
import ReactDOM from "react-dom/client";
import { createPortal } from "react-dom";
import { renderToString } from "react-dom/server";
import { sveltify } from "svelte-preprocess-react";
import YouTube from "react-youtube";
const ReactYouTube = sveltify(YouTube, {
createPortal,
ReactDOM,
renderToString,
});
const react = sveltify(
{ YouTube },
{ createPortal, ReactDOM, renderToString },
);
</script>
<ReactYouTube videoId="AdNJ3fydeao" />
<react.YouTube videoId="AdNJ3fydeao" />
```

## Setup
Expand Down Expand Up @@ -107,7 +108,7 @@ function MyComponent() {

> Extinguish
Using multiple frontend frameworks adds overhead both in User and Developer experience.
Using multiple frontend frameworks adds overhead, both in User and Developer experience.

- Increased download size
- Slower (each framework boundary adds overhead)
Expand Down
12 changes: 6 additions & 6 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ This document describes design-decisions and implementation details of the prepr

```jsx
<App>
<react:ReduxProvider value={store}>
<react.ReduxProvider value={store}>
<Layout>
<react:Info />
<react.Info />
</Layout>
</react:ReduxProvider>
</react.ReduxProvider>
</App>
```

Expand All @@ -29,9 +29,9 @@ Both Svelte and React have component trees, for context to work in both, Svelte
and React needs to act as if the tree is:

```jsx
<react:ReduxProvider value={store}>
<react:Info />
</react:ReduxProvider>
<react.ReduxProvider value={store}>
<react.Info />
</react.ReduxProvider>
```

### Client mode
Expand Down
28 changes: 28 additions & 0 deletions docs/migration-to-2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Migration to 2.0

Change `<react:MyComponent>` into `<react.MyComponent>` (`:` to `.`).

When using Typescript, add:

```ts
const react = sveltify({ MyComponent });
```

in the `<script lang="ts">` section for type safety.

When getting ESLint no-undef errors:

in `eslint.config.js` add `sveltify: true` to your globals or add a `import { sveltify } from 'svelte-preprocess-react'`.

Also add `react: true` if you don't want

## Why the change?

In Svelte 5 the compiler gives a warning when using `<react:MyComponent />` syntax:

> Self-closing HTML tags for non-void elements are ambiguous — use `<react:MyComponent ...></react:MyComponent>` rather than `<react:MyComponent ... />`(element_invalid_self_closing_tag)
Easily solved, but it a less elegant syntax.

Secondly, the new syntax allows for IDE support.

6 changes: 3 additions & 3 deletions docs/react-router.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ In src/routes/+layout.svelte
import { page } from "$app/stores";
import { goto } from "$app/navigation";
used(RouterProvider);
const react = sveltify({ RouterProvider });
</script>
<react:RouterProvider value={{ url: $page.url, params: $page.params, goto }}>
<react.RouterProvider value={{ url: $page.url, params: $page.params, goto }}>
<slot />
</react:RouterProvider>
</react.RouterProvider>
```

the actual navigation and url updates are done by the SvelteKit router. The `<RouterProvider>` exposed the current url and push & replace actions
7 changes: 6 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ export default ts.config(
languageOptions: {
ecmaVersion: 2022,
sourceType: "module",
globals: { ...globals.node, ...globals.browser },
globals: {
...globals.node,
...globals.browser,
sveltify: true,
react: true,
},
parser: svelteParser,
parserOptions: {
parser: ts.parser,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "vite dev",
"dev": "svelte-package && vite dev",
"build": "vite build",
"preview": "vite preview",
"package": "svelte-package",
Expand Down
22 changes: 22 additions & 0 deletions src/lib/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Component } from "svelte";
import type { Sveltified } from "./internal/types";

declare global {
function sveltify<
T extends {
[key: string]:
| keyof JSX.IntrinsicElements
| React.JSXElementConstructor<any>;
},
>(
components: T,
): {
[K in keyof T]: K extends keyof JSX.IntrinsicElements
? Sveltified[K]
: Sveltified<T[K]>;
};

const react: {
[component: string]: Component;
};
}
6 changes: 5 additions & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/// <reference path="global.d.ts" />

import type { Sveltified } from "./internal/types.js";
export { default as hooks } from "./hooks.js";
export { default as reactify } from "./reactify.js";
Expand All @@ -8,7 +10,9 @@ export { default as useStore } from "./useStore.js";
declare global {
function sveltify<
T extends {
[key: string]: React.FC | React.ComponentClass;
[key: string]:
| keyof JSX.IntrinsicElements
| React.JSXElementConstructor<any>;
},
>(
reactComponents: T,
Expand Down
12 changes: 8 additions & 4 deletions src/lib/internal/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type ReactDOMServer from "react-dom/server";
import type { ComponentClass, FunctionComponent, ReactNode } from "react";
import {
type ComponentClass,
type FunctionComponent,
type ReactNode,
} from "react";
import type { Root } from "react-dom/client";
import type { Component, Snippet } from "svelte";

Expand Down Expand Up @@ -76,9 +80,9 @@ export type ChildrenPropsAsSnippet<T> = T extends {
? Omit<T, "children"> & { children?: Snippet }
: T;

export type Sveltified<T extends React.JSXElementConstructor<any>> = Component<
ChildrenPropsAsSnippet<React.ComponentProps<T>>
>;
export type Sveltified<
T extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>,
> = Component<ChildrenPropsAsSnippet<React.ComponentProps<T>>>;

export type ReactDependencies = {
ReactDOM?:
Expand Down
Loading

0 comments on commit 2d7f6a3

Please sign in to comment.