├── test └── cljsx │ ├── components.js │ ├── core_test.clj │ ├── fn_macros_test.cljs │ ├── react_test.cljs │ └── spec_test.clj ├── .travis.yml ├── dev.cljs.edn ├── .gitignore ├── resources └── public │ ├── index.html │ ├── example-figwheel.html │ └── example-shadow-cljs.html ├── shadow-cljs.edn ├── package.json ├── examples ├── figwheel_example │ └── main.cljs └── shadow_cljs_example │ └── main.cljs ├── src └── cljsx │ ├── core.cljs │ ├── specs.clj │ └── core.clj ├── project.clj └── README.md /test/cljsx/components.js: -------------------------------------------------------------------------------- 1 | export const JSComponent = props => 2 | props.__hash === undefined 3 | ? "js" 4 | : "clj" 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - language: clojure 4 | script: lein travis 5 | - language: node_js 6 | node_js: "lts/*" 7 | script: npm run travis 8 | -------------------------------------------------------------------------------- /dev.cljs.edn: -------------------------------------------------------------------------------- 1 | ^{:open-url "http://localhost:[[server-port]]/example-figwheel.html"} 2 | {:main figwheel-example.main 3 | :output-dir "target/public/figwheel-out/dev" 4 | :output-to "target/public/figwheel-out/main.js"} 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nrepl-port 2 | pom.xml 3 | pom.xml.asc 4 | *.jar 5 | *.class 6 | /lib/ 7 | /classes/ 8 | /target/ 9 | /checkouts/ 10 | .lein-deps-sum 11 | .lein-repl-history 12 | .lein-plugins/ 13 | .lein-failures 14 | .nrepl-port 15 | .cpcache/ 16 | .shadow-cljs/ 17 | node_modules/ 18 | public/example* 19 | .\#* 20 | .rebel_readline_history 21 | .eastwood 22 | -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Shadow CLJS Example 9 | Figwheel Example 10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/public/example-figwheel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Figwheel Example 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/public/example-shadow-cljs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Shadow CLJS Example 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths 3 | ["src" "test" "examples"] 4 | 5 | :dependencies 6 | [[binaryage/devtools "0.9.10"] 7 | [cider/cider-nrepl "0.21.0"] 8 | [refactor-nrepl "2.4.0"] 9 | [smidjen "0.2.3"]] 10 | 11 | :dev-http 12 | {5555 ["resources/public" "target/public"]} 13 | 14 | :builds 15 | {:example {:target :browser 16 | :output-dir "target/public/shadow-cljs-out" 17 | ;; The asset-path stopped working when the example was moved 18 | ;; to ./examples. 19 | :asset-path "/shadow-cljs-out" 20 | :modules {:main {:entries [shadow-cljs-example.main]}}} 21 | :node-test {:target :node-test 22 | :output-to "target/public/node-test.js" 23 | :cache-level :off} 24 | :node-test-dev {:target :node-test 25 | :output-to "target/public/node-test.js" 26 | :autorun true}}} 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cljsx", 3 | "version": "1.0.0", 4 | "description": "JSX for Clojure and ClojureScript", 5 | "devDependencies": { 6 | "@cljs-oss/module-deps": "^1.1.1", 7 | "@material-ui/core": "^4.1.1", 8 | "@material-ui/icons": "^4.2.0", 9 | "npm-run-all": "^4.1.5", 10 | "react": "^16.8.6", 11 | "react-dom": "^16.8.6", 12 | "react-router-dom": "^5.0.1", 13 | "react-transition-group": "^4.1.1", 14 | "shadow-cljs": "^2.8.25", 15 | "warning": "^4.0.3" 16 | }, 17 | "scripts": { 18 | "check": "run-p test lint", 19 | "fig:build": "lein fig:build", 20 | "fig": "lein fig", 21 | "lint": "lein lint", 22 | "start": "shadow-cljs watch example", 23 | "test:compile": "shadow-cljs compile node-test", 24 | "test:run": "node target/public/node-test.js", 25 | "test:clj": "lein test:clj", 26 | "test:cljs": "run-s test:compile test:run", 27 | "test:watch": "shadow-cljs watch node-test-dev", 28 | "test": "run-p test:clj test:cljs", 29 | "travis": "npm run test:cljs" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/peterhudec/cljsx.git" 34 | }, 35 | "keywords": [], 36 | "author": "Peter Hudec", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/peterhudec/cljsx/issues" 40 | }, 41 | "homepage": "https://github.com/peterhudec/cljsx", 42 | "dependencies": {} 43 | } 44 | -------------------------------------------------------------------------------- /examples/figwheel_example/main.cljs: -------------------------------------------------------------------------------- 1 | (ns figwheel-example.main 2 | (:require [react :as react] 3 | [react-dom :as react-dom] 4 | [cljsx.core :refer [react> defcomponent]])) 5 | 6 | (def common-props {:style {:color "purple" 7 | :background "lavenderblush" 8 | :borderRadius "0.2em" 9 | :padding "0.4em"}}) 10 | 11 | (react> 12 | (defcomponent Button {:keys [title children] :as props} 13 | (" 955 | ``` 956 | 957 | ### Example 958 | 959 | And finally an example of a simple application which uses [Material-UI] and 960 | [React Router] written in JSX, `cljsx` and [reagent]. 961 | 962 | #### JavaScript With JSX 963 | 964 | ```jsx 965 | import React from 'react' 966 | import ReactDOM from 'react-dom' 967 | import { BrowserRouter, Link, Route } from 'react-router-dom' 968 | import { Avatar, Card, CardContent, CardHeader, IconButton } from '@material-ui/core' 969 | import * as icons from '@material-ui/icons' 970 | 971 | const IconButtonLink = props => 972 | 973 | 974 | const App = () => 975 | 976 | 977 | {({location: {pathname: path}}) => { 978 | const activeIconName = path.slice(1) 979 | const ActiveIcon = icons[activeIconName] 980 | const title = ActiveIcon ? activeIconName : "Click the icons" 981 | return ( 982 | 983 | }/> 986 | 987 | {Object.keys(icons) 988 | .filter(iconName => iconName.endsWith('TwoTone')) 989 | .map(iconName => { 990 | const Icon = icons[iconName] 991 | const color = iconName === activeIconName 992 | ? "secondary" 993 | : "default" 994 | return ( 995 | 996 | 997 | 998 | ) 999 | })} 1000 | 1001 | 1002 | ) 1003 | }} 1004 | 1005 | 1006 | 1007 | ReactDOM.render( 1008 | , 1009 | document.querySelector('#mount-point'), 1010 | ) 1011 | ``` 1012 | 1013 | #### ClojureScript With CLJSX 1014 | 1015 | ```clj 1016 | (ns shadow-cljs-example.main 1017 | (:require ["react" :as react] 1018 | ["react-dom" :as react-dom] 1019 | ["react-router-dom" :as rr] 1020 | ["@material-ui/core" :as mui] 1021 | ["@material-ui/icons" :as mui-icons] 1022 | [cljsx.core :refer [defcomponent fn-clj react>]])) 1023 | 1024 | (react> 1025 | (defcomponent IconButtonLink props 1026 | ()) 1028 | 1029 | (defcomponent App _ 1030 | ( 1031 | ( 1032 | (fn-clj [{{path :pathname} :location}] 1033 | (let [active-icon-name (subs path 1) 1034 | ^js ActiveIcon (aget mui-icons active-icon-name)] 1035 | ( 1036 | ( 1041 | ())) >) 1042 | ( 1043 | (for [icon-name (js/Object.keys mui-icons) 1044 | :when (.endsWith icon-name "TwoTone") 1045 | :let [^js Icon (aget mui-icons icon-name)]] 1046 | ( 1051 | ()))))))))) 1052 | 1053 | (react-dom/render 1054 | () 1055 | (js/document.querySelector "#mount-point"))) 1056 | ``` 1057 | 1058 | #### ClojureScript With Reagent 1059 | 1060 | ```clj 1061 | (ns shadow-cljs-example.main 1062 | (:require ["react-router-dom" :as rr] 1063 | ["@material-ui/core" :as mui] 1064 | ["@material-ui/icons" :as mui-icons] 1065 | [reagent.core :as r])) 1066 | 1067 | (defn icon-button-link [props & children] 1068 | (into [:> mui/IconButton (merge props {:component rr/Link})] 1069 | children)) 1070 | 1071 | (defn app [] 1072 | [:> rr/BrowserRouter 1073 | [:> rr/Route 1074 | (fn [js-route-props] 1075 | (let [path (-> js-route-props .-location .-pathname) 1076 | active-icon-name (subs path 1) 1077 | ActiveIcon (aget mui-icons active-icon-name)] 1078 | (r/as-element 1079 | [:> mui/Card 1080 | [:> mui/CardHeader {:title (if ActiveIcon 1081 | active-icon-name 1082 | "Click the icons") 1083 | :avatar (r/as-element 1084 | [:> mui/Avatar 1085 | (when ActiveIcon 1086 | [:> mui/Avatar 1087 | [:> ActiveIcon]])])}] 1088 | [:> mui/CardContent 1089 | (for [icon-name (js/Object.keys icons) 1090 | :when (.endsWith icon-name "TwoTone") 1091 | :let [Icon (aget mui-icons icon-name)]] 1092 | ^{:key icon-name} 1093 | [icon-button-link {:to icon-name 1094 | :color (if (= icon-name active-icon-name) 1095 | "secondary" 1096 | "default")} 1097 | [:> Icon]])]])))]]) 1098 | 1099 | (r/render 1100 | [app] 1101 | (js/document.querySelector "#mount-point")) 1102 | ``` 1103 | 1104 | [fn]: https://clojuredocs.org/clojure.core/fn 1105 | [defn]: https://clojuredocs.org/clojure.core/defn 1106 | [clj->js]: https://cljs.github.io/api/cljs.core/clj-GTjs 1107 | [hiccups]: https://github.com/teropa/hiccups 1108 | [js->clj]: https://cljs.github.io/api/cljs.core/js-GTclj 1109 | [Figwheel]: https://figwheel.org/ 1110 | [Inferno]: https://infernojs.org/ 1111 | [JSX]: https://reactjs.org/docs/introducing-jsx.html 1112 | [Material-UI]: https://material-ui.com/ 1113 | [merge]: https://clojuredocs.org/clojure.core/merge 1114 | [Nerv]: https://nerv.aotu.io/ 1115 | [Preact]: https://preactjs.com/ 1116 | [React]: https://reactjs.org/ 1117 | [React Router]: https://reacttraining.com/react-router/ 1118 | [_render props_]: https://reactjs.org/docs/render-props.html 1119 | [reagent]: http://reagent-project.github.io/ 1120 | [shadow-cljs]: http://shadow-cljs.org/ 1121 | [Snabbdom]: https://github.com/snabbdom/snabbdom 1122 | [spec]: https://clojure.org/about/spec 1123 | --------------------------------------------------------------------------------