Fence
Effortless way to avoid manual extern files when doing javascript interop from Clojurescript.
Install / Use
/learn @myguidingstar-zz/FenceREADME
fence
Effortless way to avoid manual extern files when doing javascript interop from Clojurescript.
From a compromised Clojurik dictionary:
(Warning: This section is just for fun. Feel free to skip to main documentation ahead.)
<img align="right" width="300" src="http://i1.kym-cdn.com/photos/images/original/000/412/815/2be.jpg">fence (noun)
- a structure made of w̶o̶o̶d̶ ̶o̶r̶ ̶m̶e̶t̶a̶l̶ keystrokes etc that surrounds a piece of l̶a̶n̶d̶ code or prevents p̶e̶o̶p̶l̶e̶ ̶o̶r̶ ̶a̶n̶i̶m̶a̶l̶s̶ ̶f̶r̶o̶m̶ ̶e̶n̶t̶e̶r̶i̶n̶g̶ ̶o̶r̶ ̶l̶e̶a̶v̶i̶n̶g̶ javascript symbols from getting renamed
<img align="right" width="300" src="http://imgs.xkcd.com/comics/compiling.png">
- someone who b̶u̶y̶s̶ ̶a̶n̶d̶ ̶s̶e̶l̶l̶s̶ ̶s̶t̶o̶l̶e̶n̶ ̶g̶o̶o̶d̶s̶ uses macros from a library with stolen code in it
fence (verb)
- to put a̶ ̶f̶e̶n̶c̶e̶ parentheses around something
- to fight with a l̶o̶n̶g̶ ̶t̶h̶i̶n̶ ̶s̶w̶o̶r̶d̶ short line of dots as a s̶p̶o̶r̶t̶ way to cure headaches
- to answer someone's questions in a c̶l̶e̶v̶e̶r̶ lazy way in order to g̶e̶t̶ ̶a̶n̶ ̶a̶d̶v̶a̶n̶t̶a̶g̶e̶ ̶i̶n̶ ̶a̶n̶ ̶a̶r̶g̶u̶m̶e̶n̶t̶ answer in advance with an FAQs section in README.
That random name is a complete mess ;)
Fence provides a fence.core/+++ macro that works like do
special form. Wrap it around all your javascript interop forms to
transform them automatically so you don't have to add extern files
manually.
Usage
Add fence to your Clojurescript project:
[fence "0.2.0"]
Refer to fence.core/+++ in your namespace:
(ns hello
"Calling property symbols that won't be renamed."
(:require-macros [fence.core :refer [+++]]))
and wrap all renaming-sensitive forms inside fence.core/+++ which
works like do special form.
forms that requires extern | forms that works without extern
-------------------------- | -------------------------------------------
(. js/foo bar) | (+++ (.. js/foo -bar))
(.-boo js/foo) | (+++ (.. js/foo -boo))
(.bla js/foo) | (+++ (.. js/foo bla))
(.bla js/foo x y z) | (+++ (.. js/foo (bla x y z)))
Please note +++ use clojure.walk to transform all interop forms
inside its body so the following should work too:
(defn foo []
(+++
(.-bar js/foo)
(.moreForms js/foo)
(at (any (level (.execute js/foo))))))
How it works
Imagine you have this piece of Clojurescript code:
(ns hello
"Calling property symbols that WILL be renamed."
)
(.. js/something -someAttributes aMethod (anotherMethod "arg1" "arg2"))
without extern file(s), the above code will end up with this:
something.d.b().c("arg1", "arg2");
which will fail to execute.
Instead of writing some extern files manually, just wrap that sensitive
form inside fence.core/+++ like this:
(ns hello
"Calling property symbols that won't be renamed."
(:require-macros [fence.core :refer [+++]]))
(+++ (.. js/something -someAttributes aMethod (anotherMethod "arg1" "arg2")))
and here's (part of) the result:
(function() {
var a;
a = something.someAttributes;
a = a.aMethod.call(a);
return a.anotherMethod.call(a, "arg1", "arg2");
})();
Never be afraid of javascript interop again! ^^
More in-depth technical details:
fence.core/+++ transforms all interop forms found in its body code
into fence.core/dot and fence.core/.. macros (fence version of . and
..) which in turn will expand to aget forms.
Clojurescript forms | Output javascript | Optimized in :advanced mode without extern | Optimized with extern
----------------------|---------------------------|------------------------------------------------|-------------------
(. js/foo bar) | foo.bar | renamed to a shorter name like foo.a | foo.bar
(.-boo js/foo ) | foo.boo | renamed to a shorter name like foo.b | foo.boo
(.bla js/foo) | foo.bla() | renamed to a shorter name like foo.c() | foo.bla()
(.bla js/foo "x" 1) | foo.bla("x", 1) | renamed to a shorter name like foo.d("x", 1) | foo.bla("x", 1)
Clojurescript forms | Output javascript | Optimized with/without extern
--------------------------------|--------------------------------|-----------------------------
(+++ (.. js/foo bar)) | foo["bar"] | foo.bar
(+++ (.. js/foo -boo)) | foo["boo"] | foo.boo
(+++ (.. js/foo bla)) | foo["bla"].call(foo) | foo.bla.call(foo)
(+++ (.. js/foo (bla "x" 1))) | foo["bla"].call(foo, "x", 1) | foo.bla.call(foo, "x", 1)
FAQs
- Why not a reader macro?
- I definitely want to but Clojurescript doesn't provide a
convenient, sharable way to make reader macros like Clojure with
data_readers.clj.
- Are there any performance pitfalls?
- No. Google Closure compiler will replace string versions of properties to symbol versions so the final javascript will be the same as its counterpart using extern files.
Copyright
Copyright ©2014 Hoang Minh Thang
Distributed under the Eclipse Public License, the same as Clojure. Please see the epl-v10.html file at the top level of this repo.
Related Skills
node-connect
352.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
352.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
352.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
