Skip to content

Commit

Permalink
fix: React isn't ESM, changed imports for compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
Bob Fanger committed Oct 21, 2022
1 parent 0124340 commit 2d3518c
Show file tree
Hide file tree
Showing 19 changed files with 501 additions and 750 deletions.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"type": "git",
"url": "https://github.com/bfanger/svelte-preprocess-react.git"
},
"version": "0.11.0",
"version": "0.12.0",
"license": "MIT",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -68,7 +68,6 @@
"eslint-plugin-only-warn": "^1.0.3",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-svelte3": "^4.0.0",
"happy-dom": "^6.0.4",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"postcss": "^8.4.16",
Expand All @@ -84,7 +83,7 @@
"typescript": "^4.7.4",
"vite": "^3.0.6",
"vite-tsconfig-paths": "^3.5.0",
"vitest": "^0.23.2"
"vitest": "^0.24.3"
},
"dependencies": {
"magic-string": "^0.26.2"
Expand Down
8 changes: 4 additions & 4 deletions src/lib/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { createElement } from "react";
import * as React from "react";
import { writable, type Readable } from "svelte/store";
import type ReactDOMServer from "react-dom/server";
import { getContext, onDestroy } from "svelte";
Expand Down Expand Up @@ -42,15 +42,15 @@ function standalone(
if (!renderToString) {
throw new Error("renderToString parameter is required for SSR");
}
renderToString(createElement(Hook));
renderToString(React.createElement(Hook));
return () => {};
}
const el = document.createElement("react-hooks");
const root = ReactDOMClient.createRoot?.(el);
if (root) {
root.render(createElement(Hook));
root.render(React.createElement(Hook));
} else {
ReactDOMClient.render(createElement(Hook), el);
ReactDOMClient.render(React.createElement(Hook), el);
}
return () => {
if (root) {
Expand Down
13 changes: 6 additions & 7 deletions src/lib/internal/Bridge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type React from "react";
import { createElement } from "react";
import * as React from "react";
import useStore from "../useStore.js";
import SvelteToReactContext from "./SvelteToReactContext.js";
import Child from "./Child.js";
Expand All @@ -23,7 +22,7 @@ const Bridge: React.FC<BridgeProps> = ({ createPortal, node }) => {
return null;
}
const children: React.ReactElement[] = node.nodes.map((subnode) => {
return createElement(Bridge, {
return React.createElement(Bridge, {
key: subnode.key,
createPortal,
node: subnode,
Expand All @@ -35,18 +34,18 @@ const Bridge: React.FC<BridgeProps> = ({ createPortal, node }) => {
delete props.children;
}
if (slot) {
children.push(createElement(Child, { key: "svelte-slot", el: slot }));
children.push(React.createElement(Child, { key: "svelte-slot", el: slot }));
}
if (hooks.length >= 0) {
children.push(
...hooks.map(({ Hook, key }) => createElement(Hook, { key }))
...hooks.map(({ Hook, key }) => React.createElement(Hook, { key }))
);
}
return createPortal(
createElement(
React.createElement(
SvelteToReactContext.Provider,
{ value: node.svelteInstance },
createElement(node.reactComponent, props, children)
React.createElement(node.reactComponent, props, children)
),
target
);
Expand Down
10 changes: 5 additions & 5 deletions src/lib/internal/Child.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useRef, useEffect, createElement, type FC } from "react";
import * as React from "react";

type Props = {
el: HTMLElement | undefined;
};
const Child: FC<Props> = ({ el }) => {
const ref = useRef<HTMLElement>();
useEffect(() => {
const Child: React.FC<Props> = ({ el }) => {
const ref = React.useRef<HTMLElement>();
React.useEffect(() => {
if (!ref.current) {
return;
}
Expand All @@ -15,7 +15,7 @@ const Child: FC<Props> = ({ el }) => {
ref.current.appendChild(el);
}
}, [ref, el]);
return createElement("react-child", {
return React.createElement("react-child", {
ref,
style: { display: "contents" },
});
Expand Down
4 changes: 2 additions & 2 deletions src/lib/internal/ReactWrapper.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import { writable } from "svelte/store";
import { beforeUpdate, getContext, onDestroy, setContext } from "svelte";
import type { SvelteInit, TreeNode } from "./types";
import type React from "react";
import type { FunctionComponent } from "react";
export let svelteInit: (options: SvelteInit) => TreeNode;
const props = writable<Record<string, any>>(extractProps($$props));
const target = writable<HTMLElement | undefined>();
const slot = writable<HTMLElement | undefined>();
const hooks = writable<Array<{ Hook: React.FC; key: number }>>([]);
const hooks = writable<Array<{ Hook: FunctionComponent; key: number }>>([]);
const listeners: Array<() => void> = [];
const parent = getContext<TreeNode | undefined>("ReactWrapper");
Expand Down
4 changes: 2 additions & 2 deletions src/lib/internal/SvelteToReactContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createContext } from "react";
import * as React from "react";

const SvelteToReactContext = createContext(undefined as any);
const SvelteToReactContext = React.createContext(undefined as any);
SvelteToReactContext.displayName = "SvelteToReactContext";
export default SvelteToReactContext;
5 changes: 2 additions & 3 deletions src/lib/internal/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type React from "react";
import type { ComponentClass, FunctionComponent } from "react";
import type { Readable, Writable } from "svelte/store";

Expand Down Expand Up @@ -55,7 +54,7 @@ export type TreeNode = Omit<SvelteInit, "onDestroy"> & {
svelteInstance: Readable<any>;
reactComponent: FunctionComponent<any> | ComponentClass<any>;
key: number;
hooks: Writable<Array<{ Hook: React.FC; key: number }>>;
hooks: Writable<Array<{ Hook: FunctionComponent; key: number }>>;
nodes: TreeNode[];
};

Expand All @@ -64,6 +63,6 @@ export type SvelteInit = {
props: Readable<Record<string, any>>;
target: Readable<HTMLElement | undefined>;
slot: Readable<HTMLElement | undefined>;
hooks: Writable<Array<{ Hook: React.FC; key: number }>>;
hooks: Writable<Array<{ Hook: FunctionComponent; key: number }>>;
onDestroy: (callback: () => void) => void;
};
2 changes: 1 addition & 1 deletion src/lib/preprocessReact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ function replaceReactTags(
}
}
tag.attributes.forEach((attr) => {
if (attr.type === "EventHandler") {
if (attr.type === "EventHandler" && attr.expression !== null) {
const event = attr as Transition;
if (event.modifiers.length > 0) {
throw new Error(
Expand Down
35 changes: 14 additions & 21 deletions src/lib/reactify.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
createElement,
useContext,
useEffect,
useRef,
type ReactNode,
} from "react";
import type { FunctionComponent } from "react";
import * as React from "react";
import { get, type Readable } from "svelte/store";
import SvelteWrapper from "./internal/SvelteWrapper.svelte";
import SvelteToReactContext from "./internal/SvelteToReactContext.js";
Expand All @@ -24,23 +17,23 @@ export type SvelteConstructor<Props = any, Events = any, Slot = any> = {
*/
export default function reactify<P = any, E = any>(
SvelteComponent: SvelteConstructor<P, E>
): FunctionComponent<P & SvelteEventHandlers<E>> {
): React.FunctionComponent<P & SvelteEventHandlers<E>> {
const { name } = SvelteComponent as any;
const named = {
[name](options: any) {
const { children } = options;
const props = extractProps(options);
const events = extractListeners(options);

const wrapperRef = useRef<HTMLElement>();
const svelteRef = useRef<SvelteWrapper>();
const slotRef = useRef<HTMLElement>();
const childrenRef = useRef<HTMLElement>();
const wrapperRef = React.useRef<HTMLElement>();
const svelteRef = React.useRef<SvelteWrapper>();
const slotRef = React.useRef<HTMLElement>();
const childrenRef = React.useRef<HTMLElement>();

const context = useContext(SvelteToReactContext);
const context = React.useContext(SvelteToReactContext);

// Mount Svelte component
useEffect(() => {
React.useEffect(() => {
const target = wrapperRef.current;
if (!target) {
return undefined;
Expand Down Expand Up @@ -71,14 +64,14 @@ export default function reactify<P = any, E = any>(
}, [wrapperRef]);

// Sync props & events
useEffect(() => {
React.useEffect(() => {
if (svelteRef.current) {
svelteRef.current.$set({ props, events });
}
}, [props, svelteRef]);

// Sync children/slot
useEffect(() => {
React.useEffect(() => {
if (childrenRef.current) {
if (
slotRef.current &&
Expand All @@ -101,22 +94,22 @@ export default function reactify<P = any, E = any>(
context: extractSvelteContext(context),
$$slots,
});
return createElement("svelte-wrapper", {
return React.createElement("svelte-wrapper", {
style: {
display: "contents",
},
dangerouslySetInnerHTML: { __html: result.html },
});
}

return createElement(
return React.createElement(
"svelte-wrapper",
{
ref: wrapperRef,
style: { display: "contents" },
},
children
? createElement(
? React.createElement(
"react-children",
{
ref: childrenRef,
Expand Down Expand Up @@ -164,7 +157,7 @@ function extractSvelteContext(reactContext: Readable<any> | undefined) {
}

function detectChildren(
children: ReactNode | ReactNode[] | undefined
children: React.ReactNode | React.ReactNode[] | undefined
): boolean {
if (children === undefined) {
return false;
Expand Down
20 changes: 11 additions & 9 deletions src/lib/sveltify.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createElement } from "react";
import type { ComponentClass, FunctionComponent } from "react";
import * as React from "react";
import type { SvelteComponentTyped } from "svelte/internal";
import type ReactDOMServer from "react-dom/server";
import { writable, type Readable } from "svelte/store";
Expand Down Expand Up @@ -32,7 +31,7 @@ declare type Sveltified<P extends Record<string, any>> = new (args: {
* Convert a React component into a Svelte component.
*/
export default function sveltify<P>(
reactComponent: FunctionComponent<P> | ComponentClass<P>,
reactComponent: React.FunctionComponent<P> | React.ComponentClass<P>,
createPortal: BridgeProps["createPortal"],
ReactDOMClient: any,
renderToString?: typeof ReactDOMServer.renderToString
Expand All @@ -55,14 +54,17 @@ export default function sveltify<P>(
}
const html = $$render.call(Slot, result, {}, bindings, slots, context);
const vdom = html
? createElement(
reactComponent as FunctionComponent,
? React.createElement(
reactComponent as React.FunctionComponent,
props,
createElement("svelte-slot", {
React.createElement("svelte-slot", {
dangerouslySetInnerHTML: { __html: html },
})
)
: createElement(reactComponent as FunctionComponent, props);
: React.createElement(
reactComponent as React.FunctionComponent,
props
);
return renderToString(vdom);
},
} as any;
Expand All @@ -76,11 +78,11 @@ export default function sveltify<P>(
document.head.appendChild(targetEl);
if (root) {
rerender = (props: BridgeProps) => {
root.render(createElement(Bridge, props));
root.render(React.createElement(Bridge, props));
};
} else {
rerender = (props: BridgeProps) => {
ReactDOMClient.render(createElement(Bridge, props), rootEl);
ReactDOMClient.render(React.createElement(Bridge, props), rootEl);
};
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/lib/useStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import * as React from "react";
import { get, type Readable } from "svelte/store";

/**
Expand All @@ -12,8 +12,8 @@ import { get, type Readable } from "svelte/store";
* }
*/
export default function useStore<T>(store: Readable<T>): T {
const [value, setValue] = useState(() => get(store));
useEffect(() => {
const [value, setValue] = React.useState(() => get(store));
React.useEffect(() => {
let first = true;
const cancel = store.subscribe((next) => {
if (first) {
Expand Down
2 changes: 1 addition & 1 deletion src/routes/api/react-version.json/+server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import detectReactVersion from "$lib/internal/detectReactVersion";
import type { RequestHandler } from "@sveltejs/kit";
import detectReactVersion from "$lib/internal/detectReactVersion";

export const GET: RequestHandler = async () => {
return new Response(`${await detectReactVersion()}`, {
Expand Down
2 changes: 1 addition & 1 deletion src/routes/sveltify-react/+server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import detectReactVersion from "$lib/internal/detectReactVersion";
import { redirect, type RequestHandler } from "@sveltejs/kit";
import detectReactVersion from "$lib/internal/detectReactVersion";

export const GET: RequestHandler = async () => {
const version = await detectReactVersion();
Expand Down
2 changes: 1 addition & 1 deletion src/tests/__snapshots__/preprocess.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ import React$$sveltify from \\"svelte-preprocess-react/sveltify\\"; import React
const React$button = React$$sveltify(\\"button\\", React$$createPortal, React$$ReactDOM, React$$renderToString);
</script>
<React$button onClick={() => console.info(\\"clicked\\")}>
<React$button onClick={() => console.info(\\"clicked\\")} on:keydown>
<slot />
</React$button>
"
Expand Down
2 changes: 1 addition & 1 deletion src/tests/fixtures/Element.svelte
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<react:button on:click={() => console.info("clicked")}>
<react:button on:click={() => console.info("clicked")} on:keydown>
<slot />
</react:button>
4 changes: 2 additions & 2 deletions src/tests/preprocess.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { readFile } from "fs/promises";
import { dirname, resolve } from "path";
import { describe, expect, it } from "vitest";
import { preprocess } from "svelte/compiler";
import sveltePreprocess from "svelte-preprocess";
import { readFile } from "fs/promises";
import { dirname, resolve } from "path";
import preprocessReact from "../lib/preprocessReact";

describe("svelte-preprocess-react", () => {
Expand Down
4 changes: 2 additions & 2 deletions src/tests/reactify.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import DogSvelte from "./fixtures/Dog.svelte";

describe("reactify", () => {
const Dog = reactify(DogSvelte);
type ReactProps = React.ComponentProps<typeof Dog>;
type ReactProps = React.ComponentProps<typeof Dog>; // in a tsx file, the resulting type is "any" :-(
it("renders a svelte-wrapper", () => {
const props: ReactProps = { name: "Fido", onBark() {} };
const html = renderToString(<Dog/>);
const html = renderToString(<Dog />);
expect(html).toMatchInlineSnapshot(
'"<svelte-wrapper style=\\"display:contents\\"></svelte-wrapper>"'
);
Expand Down
1 change: 0 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const config: UserConfig = {
fs: { allow: ["package"] },
},
test: {
environment: "happy-dom",
exclude: [...configDefaults.exclude, "package", "playwright"],
},
resolve: {
Expand Down
Loading

0 comments on commit 2d3518c

Please sign in to comment.