• Stars
    star
    162
  • Rank 232,284 (Top 5 %)
  • Language
    Clojure
  • Created over 8 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Command and library to convert JSX snippets to Om/Reagent/Rum or other Clojurescript-style format.

JSX to Clojurescript Β  Β  jsx Β  arrow Β  clojurescript

Moving old ReactJS codebase to Clojurescript? Tired of manually typing ReactJS examples as Clojurescript?

Search no more!

This is command utility and library to convert JSX snippets to Om/Reagent/Rum Clojurescript-style format. Note, this is by no means to be perfect JS->Cljs compiler, output will often still need touch of your loving hand, but, hey, most of dirty work will be done 😎

This library uses acorn-jsx to parse JSX into nice AST. So big kudos there. Since it's Node.js library, you can use this library only in Clojurescript targeted to Node.js

Installation

As command:

npm install -g jsx-to-clojurescript

As library:

[jsx-to-clojurescript "0.1.9"]

As Alfred workflow (Mac only):

I also made workflow, you can download it here Following things are needed:

  • You installed via npm install -g jsx-to-clojurescript
  • Workflow assumes following paths /usr/local/bin/node /usr/local/bin/jsx-to-clojurescript. If you have different, you can change it in Alfred preferences when you open this workflow's run script.

Use it with keyword jsxcljs and paste JSX. To change command line arguments for all following queries use jsxcljs set and type arguments. Don't put arguments into jsxcljs, only JSX string.

Build your own:

lein cljsbuild once

Usage

jsx-to-clojurescript -h

  Usage: jsx-to-clojurescript [options] <string>

  Converts JSX string into selected Clojurescript React library format

  Options:

    -h, --help            output usage information
    -V, --version         output the version number
    -t --target [target]  Target library (om/reagent/rum). Default om
    --ns [string]         Namespace for compoments. Default ui
    --dom-ns [ns]         Namespace for DOM compoments. Default dom
    --lib-ns [ns]         Target library ns. Default for Om: 'om'. Default for reagent & rum: 'r'
    --kebab-tags          Convert tags to kebab-case?
    --kebab-attrs         Convert attributes to kebab-case?
    --camel-styles        Keep style keys as camelCase
    --remove-attr-vals    Remove attribute values?
    --omit-empty-attrs    Omit empty attributes?
    --styles-as-vector    Keep multiple styles as vector instead of merge

Okay let's start with something simple :bowtie:

<div>
    <RaisedButton label="Secondary" secondary={true} style={style} />
    <RaisedButton label="Disabled" disabled={true} style={style} />
</div>
jsx-to-clojurescript --kebab-tags "$(pbpaste)"
(dom/div
 {}
 (ui/raised-button {:label "Secondary", :secondary true, :style style})
 (ui/raised-button {:label "Disabled", :disabled true, :style style}))

Now something more nested... πŸ˜‰

<AppBar
    title="Title"
    iconElementLeft={<IconButton><NavigationClose /></IconButton>}
    iconElementRight={
      <IconMenu
        iconButtonElement={
          <IconButton><MoreVertIcon /></IconButton>
        }
        targetOrigin={{horizontal: 'right', vertical: 'top'}}
        anchorOrigin={{horizontal: 'right', vertical: 'top'}}
      >
        <MenuItem primaryText="Refresh" />
        <MenuItem primaryText="Help" />
        <MenuItem primaryText="Sign out" />
      </IconMenu>
    }
  />
 jsx-to-clojurescript --kebab-tags --kebab-attrs --ns "u" --target reagent --omit-empty-attrs "$(pbpaste)"
[u/app-bar
 {:title "Title",
  :icon-element-left [u/icon-button [u/navigation-close]],
  :icon-element-right
  [u/icon-menu
   {:icon-button-element [u/icon-button [u/more-vert-icon]],
    :target-origin {:horizontal "right", :vertical "top"},
    :anchor-origin {:horizontal "right", :vertical "top"}}
   [u/menu-item {:primary-text "Refresh"}]
   [u/menu-item {:primary-text "Help"}]
   [u/menu-item {:primary-text "Sign out"}]]}]

Conditions and anonymous functions are okay too! πŸ˜ƒ

<div style={{width: '100%', maxWidth: 700, margin: 'auto'}}>
        <Stepper activeStep={stepIndex}>
          <Step>
            <StepLabel>Select campaign settings</StepLabel>
          </Step>
          <Step>
            <StepLabel>Create an ad group</StepLabel>
          </Step>
          <Step>
            <StepLabel>Create an ad</StepLabel>
          </Step>
        </Stepper>
        <div style={contentStyle}>
          {finished ? (
            <p>
              <a href="#"
                onClick={(event) => {
                  event.preventDefault();
                  this.setState({stepIndex: 0, finished: false});
                }}
              >
                Click here
              </a> to reset the example.
            </p>
          ) : (
            <div>
              <p>{this.getStepContent(stepIndex)}</p>
              <div style={{marginTop: 12}}>
                <FlatButton
                  label="Back"
                  disabled={stepIndex === 0}
                  onTouchTap={this.handlePrev}
                  style={{marginRight: 12}}
                />
                <RaisedButton
                  label={stepIndex === 2 ? 'Finish' : 'Next'}
                  primary={true}
                  onTouchTap={this.handleNext}
                />
              </div>
            </div>
          )}
        </div>
      </div>
jsx-to-clojurescript --kebab-tags --kebab-attrs --ns "u" --target reagent --omit-empty-attrs "$(pbpaste)"
[:div
 {:style {:width "100%", :max-width 700, :margin "auto"}}
 [u/stepper
  {:active-step step-index}
  [u/step [u/step-label "Select campaign settings"]]
  [u/step [u/step-label "Create an ad group"]]
  [u/step [u/step-label "Create an ad"]]]
 [:div
  {:style content-style}
  (if finished
    [:p
     [:a
      {:href "#",
       :on-click
             (fn [event]
               (prevent-default event)
               (r/set-state this {:step-index 0, :finished false}))}
      "Click here"]
     " to reset the example."]
    [:div
     [:p (get-step-content step-index)]
     [:div
      {:style {:margin-top 12}}
      [u/flat-button
       {:label        "Back",
        :disabled     (= step-index 0),
        :on-touch-tap handle-prev,
        :style        {:margin-right 12}}]
      [u/raised-button
       {:label        (if (= step-index 2) "Finish" "Next"),
        :primary      true,
        :on-touch-tap handle-next}]]])]]

Mapping? No problem! Notice how map doesn't require any more editing ☺️

<ul>
    {this.props.results.map(function(result) {
        return <ListItemWrapper data={result}/>;
    })}
</ul>
jsx-to-clojurescript --ns "" --target om "$(pbpaste)"
(dom/ul
 {}
 (map
    (fn [result]
        (ListItemWrapper {:data result}))
    (:results props)))

Still not impressed? We can do spread attributes too! πŸ˜€

<Animated.View
   {...this.state.panResponder.panHandlers}
   style={this.state.pan.getLayout()}>
   {this.props.children}
 </Animated.View>
jsx-to-clojurescript --ns "" --target om "$(pbpaste)"
(AnimatedView
 (merge
  (:pan-handlers (:pan-responder state))
  {:style (get-layout (:pan state))})
 (:children props))

Array of styles as a nice merge 😌

<View style={styles.container}>
   <View style={[styles.box, {width: this.state.w, height: this.state.h}]} />
</View>
jsx-to-clojurescript --target reagent "$(pbpaste)"
[ui/View
 {:style (:container styles)}
 [ui/View
  {:style
   (merge (:box styles) {:width (:w state), :height (:h state)})}]]

Reagent can do neat trick with ids and classes πŸ˜—

<div id="my-id" className="some-class some-other">
    <span className={styles.span}>
        <b className={"home"}>Home</b>
    </span>
</div>
jsx-to-clojurescript --kebab-attrs --target reagent "$(pbpaste)"
[:div#my-id.some-class.some-other
 {}
 [:span {:class-name (:span styles)} [:b.home {} "Home"]]]

LOL variable declarations and conditions?! πŸ˜‚

< Navigator initialRoute = {
    {
      name: 'My First Scene',
      index: 0
    }
  }
  renderScene = {
    (route, navigator) =>
    < MySceneComponent
    name = {
      route.name
    }
    onForward = {
      () => {
        var nextIndex = route.index + 1,
          myOtherIndex = nextIndex + 10;

        navigator.push({
          name: 'Scene ' + nextIndex,
          index: nextIndex,
        });

        var yetAnotherIndex = myOtherIndex - 1;
      }
    }
    onBack = {
      () => {
        if (route.index > 0) {
          navigator.pop();
        } else if (route.index == 0) {
          someFuction();
          namingIsHardFun();
        } else {
        	var myGreatParam = 5;
          someOtherFunction(myGreatParam);
        }
      }
    }
    />
  }
  />
jsx-to-clojurescript --kebab-attrs --kebab-tags "$(pbpaste)"
(ui/navigator
  {:initial-route {:name "My First Scene", :index 0},
   :render-scene  (fn [route navigator]
                    (ui/my-scene-component
                      {:name (:name route),
                       :on-forward
                             (fn []
                               (let [next-index (+ (:index route) 1)
                                     my-other-index (+ next-index 10)
                                     yet-another-index (- my-other-index 1)]
                                 (push navigator {:name (+ "Scene " next-index), :index next-index}))),
                       :on-back
                             (fn []
                               (if (> (:index route) 0)
                                 (pop navigator)
                                 (if (= (:index route) 0)
                                   (do
                                     (some-fuction)
                                     (naming-is-hard-fun))
                                   (let [my-great-param 5]
                                     (some-other-function my-great-param)))))}))})

No problem with regular JS πŸ˜‰

const myConst = {some: 34};

function explode(size) {
	var earth = "planet";
	var foo = "bar";
	return boom(earth) * size + myConst;
}

explode(42);
jsx-to-clojurescript "$(pbpaste)"
(do
  (def my-const {:some 34})
  (defn explode [size]
    (let [earth "planet"
          foo "bar"]
      (+ (* (boom earth) size) my-const)))
  (explode 42))

Alright folks, that's enough of examples, I guess you get the picture πŸ˜‰. If you saw error like ERROR: Don't know how to handle node type <something> it means I haven't implmented some JS syntax yet. Open issue or PR :)

Library API

I won't write API here for 2 reasons:

  1. Not sure if this has many use cases as a library

  2. Core codebase is just ~200 very straightforward lines of code. You will get it very quickly, when you see it. (gotta love Clojure πŸ’œ)

If interested, library is extendable, you can easily add other targets other than Om/Reagent (with a single function!)

More Repositories

1

cljs-react-material-ui

Clojurescript library for using material-ui.com
Clojure
205
star
2

fractalify

Repo for fractalify.com
Clojure
101
star
3

clojurescript-ethereum-example

Tutorial App
Clojure
61
star
4

gae-angular-material-starter

Easiest way to start Google App Engine Angular Material project on Earth & Mars!
Python
49
star
5

todomvc-omnext-datomic-datascript

TodoMVC via Om.Next, Datomic, Datascript
Clojure
42
star
6

catlantis

ReactNative & Clojurescript App about Cats!
Clojure
37
star
7

alfred-iterm-sessions

Alfred workflow to quickly select of one currently opened iTerm sessions
AppleScript
35
star
8

cljs-react-material-ui-example

Example app for using Om.Next and Material-UI
Clojure
31
star
9

survival-shooter-multiplayer-unity3d-files

Cute web based multiplayer shooter game made with Unity3D
C#
25
star
10

angular-globe

Interactive Angular D3 Globe directive for fun and profit and data visualization
19
star
11

emojillionaire

Ethereum based lottery with emoji theme
Clojure
18
star
12

simple-angularfire-angularmaterial-chat

Simple chat app made using AngularFire & AngularMaterial
ApacheConf
8
star
13

multiline-string-to-javascript-converter

Utility to convert multiline string into javascript variable assignment
ApacheConf
7
star
14

travelling-salesman-problem

Multithreaded Clojure solution to Travelling Salesman Problem
Clojure
6
star
15

survival-shooter-multiplayer

Cute web based multiplayer shooter game made with Unity3D
HTML
5
star
16

reagent-material-ui

Clojure
3
star
17

disapainted

Repository for disapainted.com. The webapp for creating pivot-like stickman animations
JavaScript
3
star
18

meteor-angular-material-todomvc

Example of simple Meteor Angular Material TODO app
JavaScript
3
star
19

ethlance-emailer

Clojure
2
star
20

my-intellij-settings

Repository for my shared IntelliJ Platform Settings
JavaScript
2
star
21

node-base64resize

Nodejs module, which resizes base64 image input and returns new base64 string or saves image into filesystem
JavaScript
2
star
22

js-to-edn

Simple command line utility to convert Javascript data structure (map or array) or JSON to formatted EDN.
Clojure
1
star
23

omnext-resources

Om.Next Resources: Tutorials, Examples, Demos, Apps and Libraries
1
star
24

datascript-fulltext-test

Clojure
1
star
25

tweet-sentiment

Simple Clojure(script) app that fetches tweets and determines their sentiment via Dandelion API
JavaScript
1
star
26

thai2english-chrome-extension

Chrome extension which uses thai2english.com to translate Thai to English
Clojure
1
star