Opinionated Dart to Clojure converter for Flutter widgets. It doesn't (and probably will not) convert classes, setters, or annotations—only the parts that can be reused after translation.
There are 9 options now:
- Calva (VSCode);
- Intellij Idea;
- Jvm/Js REPL;
- Clojure Cli;
- Jar;
- Native image;
- Native image, Emacs;
- NPM CLI;
- NPM library;
The converted code would not be idiomatic. Instead of using classes, there is a widget macro.
Setters are also useless; there would be a :state
atom
and swap!
or reset!
functions for changing the state.
So I see little value in converting everything.
But rewriting widgets is routine and most of the time translates literally:
Center(
child: Text(
_active ? 'Active' : 'Inactive',
style: const TextStyle(fontSize: 32.0, color: Colors.white),
),
);
(m/Center
.child (m/Text
(if _active "Active" "Inactive")
.style (m/TextStyle .fontSize 32.0 .color m.Colors/white)))
Three more examples:
- constructor invocation;
- static, factory methods invocation;
- named arguments;
- (typed) lists, maps, cascade operator (
..
); - math and logical expressions;
- ternary operators;
- lambdas;
- comments (will be removed);
- nesting children with the f/nest macro;
- constants;
- variables in strings with $;
- raw (interpreted) strings like
r'some string $dollar'
- class/methods/fields declarations (not tested well, pre-alpha);
- try-catch;
- for in;
- switch with breaks and returns;
- bitwise operators;
- proper aliases for everything, it's not possible to get this info generally;
- enums;
- external keyword;
- yield;
- exports;
- switch with continue, without breaks or returns (considered unidiomatic);
- for with declaration, condition and increment (considered unidiomatic)
- while (considered unidiomatic)
- early exits from lambdas (ignored);
...
operator (ignored);- typedefs (ignored);
- annotations (ignored);
- test classes and methods extensively;
- handle early exit from lambdas/if/for/methods with
return
;
Calva (a VSCode Clojure extension) packs DartClojure conversion into a command. This makes it easy to paste some Dart code and convert it.
calva-dart-clojure-conversion.mp4
See Calva ClojureDart docs for a little more on the subject.
You can use the available api (4, 5, 6, 7) directly from IDEA with External Tools.
Preferences —> Tools —> External Tools —> +
Program
: any terminal command. For example, if you have npm, install
dartclojure globally and put dartclojure
in the Program
field. If you
downloaded the native image, put the path to the native image here:
/Users/PcName/Downloads/dartclojure-aarch64-darwin
Arguments
: "$SelectedText$"
That's it. Select code, press Shift + Shift
(or Cmd + Shift + A
), type DartClojure
and press Enter (or set up a hotkey for it). Here's a little video showing how it looks.
Add CLI/deps:
{:deps
{
org.clojars.liverm0r/dartclojure {:mvn/version "0.2.23-SNAPSHOT"}
}}
Or Leiningen/Boot:
[org.clojars.liverm0r/dartclojure "0.2.23-SNAPSHOT"]
Convert Dart code (simplify and wrap-nest under the hood):
(require '[dumch.convert :refer [convert]])
(convert "1 + 1 + 2 * 1;" :format :sexpr) ; => (+ 1 1 (* 2 1))
You may pass aliases for material and flutter-macro:
(convert "Text('1')" :material "m" :flutter "f") ; => "(m/Text "1")"
If you just need to wrap Clojure code with nest:
(require
'[dumch.improve :as impr]
'[rewrite-clj.zip :as z])
(-> "(Container .child (Box .child (Padding .child (:Text \"2\"))))"
z/of-string
impr/wrap-nest
z/sexpr)
; => (f/nest (Container) (Box) (Padding) (:Text "2"))
clojure -Sdeps \
'{:deps {org.clojars.liverm0r/dartclojure {:mvn/version "0.2.23-SNAPSHOT"}}}' \
-e "(require '[dumch.convert :refer [convert]]) (convert \"Text('1')\" :material \"m\" :flutter \"f\")"
There are two ways to interact with the jar. The first is to run it each time:
$ java -jar dartclojure.jar "Divider(
height: 0,
color: Colors.grey,
)"
(m/Divider .height 0 .color m.Colors/grey)
The second is to run REPL-like console (and send code into it with your editor/IDE):
$ java -jar dartclojure.jar -r true -m "material"
Paste dart code below, press Enter and see the result:
Divider(
height: 0,
color: Colors.grey,
)
(material/Divider .height 0 .color material.Colors/grey)
Colors are also supported:
For example, you may start the REPL-like console app with -e
key:
$ java -jar dartclojure.jar -r true -e :end
For all the arguments, see:
$ java -jar dartclojure.jar -h
Same API as with the jar:
./dartclojure -h
./dartclojure "Text('a')" -m "m"
./dartclojure --path main.dart
./dartclojure -r true
If there is no build for your architecture on release page, see how to build it with graalvm below.
You can use the Emacs package from dartclojure.el via your favorite Emacs package manager.
Same API as with the jar and native image:
npm i dartclojure
npx dartclojure -h
npx dartclojure "Text('a')" -m "m"
You can, of course, also install it globally:
npm i -g dartclojure
dartclojure -h
dartclojure "Text('a')" -m "m"
const converter = require('dartclojure');
const clojureCode = converter.convert("Text('a')");
Build the jar:
$ clj -T:build uber :version '"0.2.22"'
Run tests:
$ clj -X:test ; clj -M:test-cljs
Run Docker, build the GraalVM image:
$ cp target/dartclojure*.jar dartclojure.jar
$ docker build --pull --no-cache --tag dartclojure .
$ docker run --name dc -it dartclojure ./dartclojure "Text('should work')"
$ docker cp dc:/usr/src/app/dartclojure.
Compile a native image with GraalVM locally:
# install graalvm
$ chmod +x compile.sh
$ ./compile.sh
Remember to comment out the line with -H:+StaticExecutableWithDynamicLibC
for OSX M1
builds.
Start the shadow-cljs watcher and REPL:
clj -M:shadow-cljs watch :app :lib :test
(You can use this for developing both the Clojure and ClojureScript library, but the test watcher will only be watching and running the ClojureScript tests.)
Install the local npm package:
npm link dartclojure
Copyright © 2022 Artur Dumchev
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.