Skip to content

D00mch/DartClojure

Repository files navigation

DartClojure

Clojars Project

Opinionated Dart to Clojure converter for Flutter widgets. It doesn't (and probably will not) convert classes, setters, annotations — only the part that could be reused after translation.

alt text

How to use

There are 7 options now:

  1. Calva (VSCode);
  2. Intellij Idea;
  3. Jvm/Js REPL;
  4. Clojure Cli;
  5. Jar;
  6. Native image;
  7. Native image, emacs;
  8. NPM CLI;
  9. NPM library;

Why is it not a full dart->clojure converter?

All 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 a routine part 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)))

3 more examples:

Screen Recording

Supported

  • constructors 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 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;

Not supported

  • bitwise operators;
  • proper aliases for everything, it's not possible to get this info generally;
  • enums;
  • external keywork;
  • yield;
  • exports;
  • switch with continue, withouth breaks or returns (considered unidiomatic);
  • for with declare, conditions and increment (considered unidiomatic)
  • while (considered unidiomatic)
  • early exits from lambdas (ignored);
  • ... operator (ignored);
  • typedefs (ignored);
  • annotations (ignored);

TODO:

  • test classes and methods extensively;
  • handle early exit from lambdas/if/for/methods with return;

How to use

API from Calva

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 some (tiny) bit more on the subject.

API from Intellij Idea

You could use 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 native image, put path to native image here: /Users/PcName/Downloads/dartclojure-aarch64-darwin

Arguments: "$SelectedText$"

image

Thats it. Select code, press shift + shift (or cmd + shift + a), type DartClojure and press enter (or setup hotkey on it). Little video how it looks.

API from jvm/js REPL

Clojars.

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"))

API from cli

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\")"

API with Jar

There are two ways to interact with the jar. First one is to run it each time:

$ java -jar dartclojure.jar "Divider(
    height: 0,
    color: Colors.grey,
)"

(m/Divider .height 0 .color m.Colors/grey)

Second one is to run repl-like console (and send code into it with your editor/idea):

$ 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:

image

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

API with native image:

Same api as with 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.

API with native image via Emacs

You can use the Emacs package from dartclojure.el via your favorite Emacs package manager.

API with npm CLI

Same api as with jar and native image:

npm i dartclojure

npx dartclojure -h

npx dartclojure "Text('a')" -m "m"

You can of course also install globally:

npm i -g dartclojure

dartclojure -h

dartclojure "Text('a')" -m "m"

API with npm library

const converter = require('dartclojure');

const clojureCode = converter.convert("Text('a')");

Contribution

Build jar:

$ clj -T:build uber :version '"0.2.22"'

Run tests:

$ clj -X:test ; clj -M:test-cljs

Run docker, build 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 native image with graalvm locally:

# install graalvm
$ chmod +x compile.sh
$ ./compile.sh

Remember to comment out line with -H:+StaticExecutableWithDynamicLibC for osx m1 builds.

Start 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

License

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.