- Notifications
You must be signed in to change notification settings - Fork790
Google Summer of Code 2015
- Title: ClojureScript & Google Closure (Project Idea)
- Mentor: David Nolen (swannodette)
- Student: Maria Geller (mneise)
This Google Summer of Code Project will focus on improving the integrationof ClojureScript with the existing JavaScript ecosystem. The first part ofthis project will be to add CommonJS, AMD and ECMAScript 6 module support,meaning that JavaScript modules can be included and used in a ClojureScriptproject. An important part in solving this problem will be to get familiarwith the Google Closure Compiler as it provides the functionality to processJavaScript modules.The second part of this project will focus on generating extern files fornon-Closure compatible JavaScript libraries to simplify the use ofJavaScript libraries in a ClojureScript project.
This project can be split up into two parts: module support and extern generation.
Currently, it is not possible to directly include a CommonJS, AMD orECMAScript 6 JavaScript module into a ClojureScript project. To add supportfor JavaScript modules we can use functionality that is already provided bythe Google Closure Compiler. The Google Closure Compiler offers thefunctionality to transform CommonJS and ECMAScript 6 modules to GoogleClosure libraries and to transform AMD modules to CommonJS modules. Tochoose the right compilation settings, we can extend the ClojureScriptcompiler options. The ClojureScript compiler already provides the compileroption:foreign-libs
to include non-Closure compatible JavaScriptlibraries:
:foreign-libs [{:file"hello.js":provides ["my.hello"]}]
This option could be extended with an optional:module-type
key which caneither have the value:commonjs
,:amd
or:es6
:
:foreign-libs [{:file"hello.js":provides ["my.hello"]:module-type:commonjs}]
Similar to other JavaScript libraries, a JavaScript module can be added to aClojureScript namespace by requiring it with the name that has been specifiedas the value under the :provides key:
(nshello.core (:require [my.hello]))
Once a module has been transformed into a Closure library, it can be addedas a dependency withgoog.require()
which is supplied by Closure Library'sdependency management system.
We also need to be able to map the namespace of the module, which will begenerated by the Google Closure Compiler, and the namespace the user isusing in the ClojureScript project. The following example shows how a simpleCommonJS module might be transformed by the Google Closure Compiler.
var my = {};my.hello = { greet: function(name) { return"Hello" + name; }};exports.hello = my.hello;
converts to:
goog.provide("module$hello");varmodule$hello={};varmy$$module$hello={};my$$module$hello.hello={greet:function(name){return"Hello "+name;}};module$hello.hello=my$$module$hello.hello;
In this example we would need to mapmodule$hello
tomy.hello
. Fortunately,the Google Closure Compiler provides the methodtoModuleName
in the classesProcessCommonJSModules
andProcessEs6Modules
which we can use to get amodule name for a JavaScript module:
(ProcessCommonJsModules/toModuleName"hello.js");; returns module$hello
At the moment, to be able to use a non-Closure compatible JavaScript libraryin a ClojureScript project, we also need to provide an extern file if wewant to make use of the advanced compilation option which is provided by theGoogle Closure Compiler. This extern file includes the symbols that we arecalling from our ClojureScript code. Providing an extern file will stop theGoogle Closure Compiler from renaming calls to external symbols.In ClojureScript code, all calls to external JavaScript libraries have to beprefixed withjs/
:
(.greet js/my.hello"World!")
This makes it possible to identify calls to external libraries. The previouscode can also be written as:
(defhello (.-hello js/my))(.greet hello"World!")
This means we need to track that hello is a JavaScript object and need to beable to infer that resulting calls to it are external calls. This can beachieved by annotating the AST during the analyze phase. Using this information we could then generate the extern files during the compilation phase.
Until ECMAScript 6 there was no built-in support for modules in JavaScript.CommonJS and AMD are both module specifications that have been created dueto the lack of a modules system in JavaScript. With Node.js and RequireJSthose module systems have become quite popular and there are many librariesthat target one of those or even both module specifications.Adding CommonJS, AMD and ECMAScript 6 support to ClojureScript would allowusers to reuse JavaScript libraries that have been written with a specificmodule system in mind.
Currently, there are different efforts to help with the generation of externfiles, such as lein-externs and CLJSJS. While those projects make it easier toinclude extern files for JavaScript libraries, it means that a user needs tobe aware that an extern file is needed and also requires the user to choosethe right tool for it. Adding the ability to the ClojureScript compiler togenerate those extern files would lower the barrier for including externalJavaScript libraries, especially for someone who is new to ClojureScript.
At the end of this project it should be possible to include CommonJS, AMDand ECMAScript 6 modules into a ClojureScript project. Furthermore, externfiles for all observed method invocations and property accesses on externalJavaScript libraries should be generated by the ClojureScript compiler.
- Rationale
- Quick Start
- Differences from Clojure
- [Usage of Google Closure](Google Closure)