Git Product home page Git Product logo

solid-codemod's People

Contributors

davedbase avatar trivikr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

solid-codemod's Issues

[Feature]: Transform ReactJS useRef into simple let

Input code

import React, { useRef } from "react";
import { render } from "react-dom";

const Component = () => {
  const ref = useRef();
  useEffect(() => {
    ref.current.style.color = "purple";
  }, []);
  return <div ref={ref}>Hello!</div>;
};

render(() => <Component />, document.getElementById("app"));

Expected Output

import { render } from "solid-js/web";

const Component = () => {
  let ref;
  useEffect(() => {
    ref.style.color = "purple";
  }, []);
  return <div ref={ref}>Hello!</div>;
};

render(() => <Component />, document.getElementById("app"));

Additional context

In addition to removing useRef and replacing const with let, ideally we could remove the .current suffix after every ref.

A challenge is that refs shouldn't be passed into functions though; you'd need to wrap them in functions.

[Feature]: Undestructuring of props and automatic splitProps

Input code

import React from "react";

function Component1({name, count}) {
  return <div>Hello {name}! Count is {count}</div>;
}

function Component2({name, count, ...rest}) {
  return <div {...rest}>Hello {name}! Count is {count}</div>;
}

render(() => <Component />, document.getElementById("app"));

Expected Output

import { splitProps } from "solid-js";

function Component1(props) {
  return <div>Hello {props.name}! Count is {props.count}</div>;
}

function Component2(props) {
  const [local, rest] = splitProps(props, ["name", "count"]);
  return <div {...rest}>Hello {local.name}! Count is {local.count}</div>;
}

Additional context

Related work on the Babel front:

react2solid converters

actual tools to convert from react to solid

suid - A port of Material-UI (MUI) built with SolidJS.
suid/codemod - Transform your MUI React code to SUID SolidJS. (live demo)

solid-reactor - A compiler to ease the move from React to SolidJS.

react2solid - from scratch?

eslint-plugin-react2solid - based on eslint
some simple transforms are working
im currently working on React.useRef (#3) done
ideally mitosis would work better, then i would transform react โ†’ mitosis โ†’ solid
but i guess its hard to make that lossless

related

sveltosis - convert svelte to mitosis. parser based on svelte parser

component-party - Web component JS frameworks overview by their syntax and features

reactjs-solidjs-bridge

List of React APIs that are possibly not convertible/compatible to SolidJS

Here are the list of features from React that may not be possible to be converted into SolidJS code and their possible workarounds

react

  • cloneElement - Probably Element.cloneNode, but this highly depends on the usage and only works for DOM nodes not components.
  • createFactory
  • isValidElement - the lack of VDOM makes this impossible, however, instanceof Element can be a valid workaround in some cases.
  • createRef - we can probably create a library shim such that this one exists and still returns the object, but we can convert those ref property usages into a callback assignment e.g. (el) => ref.current = el.
  • forwardRef - Can be removed or provide a library shim, which ever works.
  • memo - There's already a memo function in SolidJS but it behaves differently. We can remove this during compilation since it is totally unnecessary.
  • useReducer - provide a library shim
  • useRef - we can remove this and change VariableDeclaration, but that also requires current property accesses.
  • useLayoutEffect - I'm not sure if createRenderEffect can replace this.
  • useImperativeHandle - library shim
  • useCallback - can be removed
  • useDebugValue - statement can be removed.
  • useDeferredValue - Now this one is highly different from createDeferred. We'll probably need Ryan's consultation.
  • Mutable Sources - library shim
  • useInsertionEffect - I'm not sure what this is, but I believe it is discussed in the <style> guide for React in the working group
  • useSyncExternalStore - library shim
  • Fragment - library shim, but I think I've suggested Ryan to add this one into the SolidJS core.

react-dom

  • findDOMNode
  • unmountComponentAtNode - This one's impossible.
  • flushSync - not sure what this is for, probably for flushing updates.
  • unstable_renderSubtreeIntoContainer - I think this one was used in React Server Components, but given we don't have VDOM, this one's impossible.
  • unstable_scheduleHydration
  • unstable_flushControlled

[Feature]: Convert React {array.map()} expressions to <For>

Input code

export const Looper = () => {
  return <div>
    {array.map(el => <div>{el.name}</div>)}
  </div>;
}

Expected Output

import { For } from 'solid-js';

export const Looper = () => {
  return <div>
    <For each={array}>
      {(el => <div>{el.name}</div>)}
    </For>
  </div>;
}

Additional context

Here's a codemod which does this:

import {
  ASTPath,
  CallExpression,
  FunctionExpression,
  ImportDeclaration,
  MemberExpression,
  Transform,
} from 'jscodeshift';

const transform: Transform = (file, api, options) => {
  const j = api.jscodeshift;
  const root = j(file.source);
  const importsRequired = new Set<string>();

  // Replace JSX expression {<expression>.map(fn)} with <For each={expression}>{fn}</For>
  root
    .find(j.JSXExpressionContainer)
    .filter(path => {
      // We only care about JSX expression containers whose parents are JSX elements.
      // (in other words, exclude attribute expressions.)
      if (
        path.parent.node.type === 'JSXElement' &&
        path.node.expression.type === 'CallExpression'
      ) {
        const call = path.node.expression;
        if (
          call.callee.type === 'MemberExpression' &&
          call.callee.property.type === 'Identifier' &&
          call.callee.property.name === 'map'
        ) {
          return true;
        }
      }
      return false;
    })
    .replaceWith(path => {
      const call = path.node.expression as CallExpression;
      const callee = call.callee as MemberExpression;
      importsRequired.add('For');
      return j.jsxElement.from({
        openingElement: j.jsxOpeningElement(j.jsxIdentifier('For'), [
          j.jsxAttribute.from({
            name: j.jsxIdentifier('each'),
            value: j.jsxExpressionContainer.from({
              expression: callee.object,
            }),
          }),
        ]),
        closingElement: j.jsxClosingElement(j.jsxIdentifier('For')),
        selfClosing: false,
        children: [
          j.jsxExpressionContainer.from({
            expression: call.arguments.at(0) as FunctionExpression,
          }),
        ],
      });
    });

  // See whether we need to add any imports
  if (importsRequired.size > 0) {
    // Look for solid-js import
    let lastImport: ASTPath<ImportDeclaration> | null = null;
    let solidImport = root
      .find(j.ImportDeclaration)
      .forEach(path => {
        lastImport = path;
      })
      .filter(path => path.node.source.value === 'solid-js')
      .forEach(path => {
        if (!path.node.specifiers) {
          path.node.specifiers = [];
        }
        path.node.specifiers?.forEach(spec => {
          if (spec.type === 'ImportSpecifier') {
            importsRequired.delete(spec.imported.name);
          }
        });

        for (const symbol of importsRequired) {
          path.node.specifiers.push(
            j.importSpecifier.from({
              imported: j.jsxIdentifier(symbol),
            })
          );
        }
      });

    if (solidImport.length == 0) {
      // Insert new solid import statement.
      const solidImport = j.importDeclaration.from({
        source: j.stringLiteral('solid-js'),
        specifiers: Array.from(importsRequired).map(symbol =>
          j.importSpecifier.from({
            imported: j.jsxIdentifier(symbol),
          })
        ),
      });
      lastImport!.insertAfter(solidImport);
    }
  }

  return root.toSource({ quote: 'single' });
};

export default transform;

[Feature]: Transform useState/useEffect of ReactJS to createSignal/onCleanup of SolidJS.

Input code

import React, { useState, useEffect } from "react";
import { render } from "react-dom";

const CountingComponent = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const interval = setInterval(() => setCount(count + 1), 1000);
    return () => {
      clearInterval(interval);
    };
  }, []);
  return <div>Count value is {count}</div>;
};

render(() => <CountingComponent />, document.getElementById("app"));

Expected Output

import { createSignal, onCleanup, onMount } from "solid-js";
import { render } from "solid-js/web";

const CountingComponent = () => {
  const [count, setCount] = createSignal(0);
  onMount(() => {
    const interval = setInterval(() => setCount(count + 1), 1000);
    onCleanup(() => clearInterval(interval));
  });
  return <div>Count value is {count()}</div>;
};

render(() => <CountingComponent />, document.getElementById("app"));

Additional context

Example transform to convert useState/useEffect of ReactJS to createSignal/onCleanup of SolidJS.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.