A language that lets you write JavaScript code in Clojure syntax. It combines the simplicity of Clojure syntax with the power of JavaScript libraries.
clojs
artifacts are released to Clojars.
If you are using Maven, add the following repository definition to your pom.xml
:
<repository>
<id>clojars.org</id>
<url>http://clojars.org/repo</url>
</repository>
With Maven:
<dependency>
<groupId>clojs</groupId>
<artifactId>clojs</artifactId>
<version>0.1.4</version>
</dependency>
npm
(node.js package manager) is installed with node.js
. escodegen
can be installed by typing:
npm install escodegen
in your terminal/command line.
Please open issues against the cloJS repo on Github.
Please ask questions on the cloJS mailing list.
This is for:
- JavaScript programmers who want to use simple LISP like syntax of clojure. All the JavaScript functions can be called.
- Clojure programmers who want to use JavaScript functions and libraries without learning JavaScript syntax.
You write Clojure syntax code using JavaScript functions and it is converted into JavaScript code.
The namespace for code conversion in the clojs
library is clojs.clojs
.
(require '[clojs.clojs :refer [convert convert-string] :as cj])
Convert a code snippet as string with convert-string
. It takes a string(Clojure code) and returns a string(JavaScript code):
(convert-string "(def a 1)")
=> const a = 1;
Convert one or more files containing code with convert
. It takes the path of each input file as a string and creates the equivalent JavaScript file(.js) in the same folder as the input file:
(convert "a.clj" "resources/b.clj")
=> nil
This will create a.js
in the folder where a.clj
is present and b.js
in the resources
folder where b.clj
is present.
Note: If a.js
or b.js
are already present in the respective folders, they will be overwritten.
Here's a sample Clojure app that shows how to use cloJS
A todo app written in cloJS language.
Here is all the syntax that clojs
supports:
Legend: a -> b
means a
in Clojure is converted to b
in JavaScript
Input literal type | Input | Output | Output literal type |
---|---|---|---|
String | "abc" |
'abc' |
String |
Number | 123 , 123.456 |
123 , 123.456 |
Number |
Nil | nil |
null |
Null |
Boolean | true , false |
true , false |
Boolean |
Note
null
in JavaScript isnil
in Clojure.- Strings in Clojure use only
""
as delimiters.
Identifiers like variable names, function names are converted as it is.
Note to Clojure programmers: Do not use -
in variable/function names like string-length
because running the resulting JavaScript code will error out as JavaScript use infix notation and -
is not allowed in identifiers. Use _
instead like string_length
.
["abc" 123 nil true false [1 2 3]]
->
['abc', 123, null, true, false, [1, 2, 3]]
No commas in Clojure. Nesting is supported.
{"a" 1 "b" {10 "t" 20 "f"} c 3 "d" [1 2 3]}
->
{'a': 1, 'b': { 10: 't', 20: 'f'}, c: 3, 'd': [1, 2, 3]}
No semicolons or commas in Clojure. Nesting is supported.
Note: Literals, identifiers, vectors, objects are not supported at the top-level. They have to be inside parentheses.
(def a 1 b "xyz" c nil d true e [1 2 3] f a g {a 1})
->
const a = 1, b = 'xyz', c = null, d = true, e = [1, 2, 3], f = a, g = {a: 1}
(def h e[0] i g.a j g["a"])
->
const h = e[0], i = g.a, j = g['a'];
Note: Currently only 1D arrays are supported i.e. a[1][2]
wont work.
As Clojure uses prefix notation you can give any number of arguments to the operators. clojs
requires them to be greater than or equal to two.
Input | Output |
---|---|
(+ a b 1 1.2 c) |
a + b + 1 + 1.2 + c |
(- a b 1 1.2 c) |
a - b - 1 - 1.2 - c |
(* a b 1 1.2 c) |
a * b * 1 * 1.2 * c |
(/ a b 1 1.2 c) |
a / b / 1 / 1.2 / c |
(mod a b 1 1.2 c) |
a % b % 1 % 1.2 % c |
(< a b 1 1.2 c) |
a < b < 1 < 1.2 < c |
(<= a b 1 1.2 c) |
a <= b <= 1 <= 1.2 <= c |
(> a b 1 1.2 c) |
a > b > 1 > 1.2 > c |
(>= a b 1 1.2 c) |
a >= b >= 1 >= 1.2 >= c |
(= a b 1 1.2 c) |
a === b === 1 === 1.2 === c |
(== a b 1 1.2 c) |
a == b == 1 == 1.2 == c |
(!== a b 1 1.2 c) |
a !== b !== 1 !== 1.2 !== c |
(!= a b 1 1.2 c) |
a != b != 1 != 1.2 != c |
(in a b 1 1.2 c) |
a in b in 1 in 1.2 in c |
(instanceof a b 1 1.2 c) |
a instanceof b instanceof 1 instanceof 1.2 instanceof c |
(and a b 1 1.2 c) |
a && b && 1 && 1.2 && c |
(or a b 1 1.2 c) |
a || b || 1 || 1.2 || c |
(assign a b) |
a = b |
Input | Output |
---|---|
(not a) |
!a |
(typeof a) |
typeof a |
(if (= n 0)
true
false)
if (n === 0)
true;
else
false;
(console.log 1 2 "abc" a b)
->
console.log(1, 2, "abc", a, b);
No commas in Clojure.
(if (= 1 1)
(do (console.log "x :" x)
(console.log "y :" y)
(console.log "z :" z))
(do (console.log "a :" a)
(console.log "b :" b)
(console.log "c :" c))
->
if (1 === 1) {
console.log('x :', x);
console.log('y :', y);
console.log('z :', z);;
} else {
console.log('a :', a);
console.log('b :', b);
console.log('c :', c);
}
(defn factorial [n]
(if (= n 0)
1
(* n (factorial (- n 1)))))
->
const factorial = n => {
if (n === 0)
return 1;
else
return n * factorial(n - 1);
};
Return is implicit. The last statement is the return statement.
(fn [x]
(console.log x)
(console.log (+ 5 x)))
->
x => {
console.log(x);
return console.log(5 + x);
};
(fn [x]
(console.log x)
(let a 2 b 5)
(console.log (+ 5 x a b)))
->
x => {
console.log(x);
let a = 2, b = 5;
return console.log(5 + x + a + b);
};
(return a)
->
return a;
(.attr (.parent ($ this)) "id")
->
$(this).parent().attr('id');
(fn [x]
(cond
(is_array_member form) (do (get_array_member form) (+ 1 2))
(is_defn form) (get_defn form)
(is_def form) (get_const form)
(is_if form) (get_if form)
(is_do form) (get_do form)
(is_vec form) (get_vec form)
(is_lambda form) (get_lambda form)
(is_map_ds form) (get_map_ds form)
(is_literal form) (get_literal form)
(is_operator form) (get_operator form)
(is_fn_call form) (get_fn_call form)
true nil))
->
x => {
if (is_array_member(form)) {
get_array_member(form);
return 1 + 2;
} else if (is_defn(form))
return get_defn(form);
else if (is_def(form))
return get_const(form);
else if (is_if(form))
return get_if(form);
else if (is_do(form))
return get_do(form);
else if (is_vec(form))
return get_vec(form);
else if (is_lambda(form))
return get_lambda(form);
else if (is_map_ds(form))
return get_map_ds(form);
else if (is_literal(form))
return get_literal(form);
else if (is_operator(form))
return get_operator(form);
else if (is_fn_call(form))
return get_fn_call(form);
else if (true)
return null;
};
(defmacro m-bind [mv mf]
(conj (list ~mv test) ~mf))
(m-bind mvv mff)
->
mff(mvv, test);
You can see a converted sample containing all the syntax: all.clj
-> all.js
- Ramshreyas's seqingclojure - it makes the AST for the input code.
clojs
uses this AST to convert it into an equivalent JavaScript AST in JSON format.- estool's npm package escodegen - it converts the JavaScript AST into JavaScript code.
Released under the Eclipse Public License: https://github.com/puneetpahuja/cloJS/blob/master/LICENSE