Simple es6 JS file fails to compile as foreign lib

420 views
Skip to first unread message

Zalan Kemenczy

unread,
Nov 15, 2017, 8:33:31 PM11/15/17
to ClojureScript

I'm trying to compile a super simple es6 JS file as a foreign lib, as described in the clojurescript docs under Bundling "Google Closure Compatible Code": https://clojurescript.org/reference/dependencies#bundling-javascript-code


The compilation succeeds with no optimizations. However, for the life of me I can't get it to compile with advanced optimizations. I just get a bunch of warnings, that say all the compiler phases were skipped. A lot of warnings like:


WARNING: Skipping pass checkVariableReferences

factory features:  [block function, getters, string continuation, trailing comma, setters, ES3 keywor

ds as identifiers, reserved words as properties, RegExp flag 'y', array pattern rest, binary literal,

 let declaration, computed property, super, spread expression, RegExp flag 'u', default parameter, cl

ass, arrow function, generator, template literal, for-of loop, member declaration, destructuring, oct

al literal, const declaration, extended object literal, rest parameter, new.target, exponent operator

 (**), trailing comma in param list, async function]    


The JS file looks like:


goog.provide('js.lib');


js.lib.debugMessage = (x = "any old string") => {

console.log(`Printing ${x} from cljs!`);

};


The cljs file looks like:


(ns integrated.core

  (:require [js.lib :as lib]))


(enable-console-print!)


(lib/debugMessage "Narf")


and using the standalone cljs.jar, my build.clj file is:


(require 'cljs.build.api)


(cljs.build.api/build "src"

    {:main 'integrated.core

     :output-to "out/main.js"

     :language-in :es6

     :foreign-libs [{:file "src"

                     :module-type :es6}]

     :optimizations :advanced

     :verbose true})


If this is not how it's done, how do folks get es6 js to load as a foreign lib?

francis....@gmail.com

unread,
Nov 16, 2017, 11:30:55 AM11/16/17
to ClojureScript
From the Closure repository https://github.com/google/closure-compiler/wiki/ECMAScript6 I get that "Optimizations currently occur on "transpiled" code."
So you probably need to transpile your javascript first (using babel https://babeljs.io/ for instance)

Zalan Kemenczy

unread,
Nov 16, 2017, 2:22:01 PM11/16/17
to ClojureScript
Thanks for the response.

Are you sure? The page you link to says that "ECMAScript 6 is now officially supported as an input language for the Closure Compiler", and that the google closure parser "understands all ES6 features", specifically "transpilation of the following ES6 features... arrow functions... default parameters... template strings." Which are the three es6 features included in my simple JS file.

What's more, if you remove the :language-in :es6 from the compiler options, the compiler specifically prints an error:

ERROR - this language feature is only supported for ECMASCRIPT6 mode or better: arrow function. Use --language_in=ECMASCRIPT6 or ECMASCRIPT6_STRICT or higher to enable ES6 features.

All of this seems to suggest that the google closure compiler is indeed aware of, and able to parse es6, and that something else after transpilation is causing the optimization phases to be skipped.

Z.

Thomas Heller

unread,
Nov 20, 2017, 9:36:07 AM11/20/17
to ClojureScript
The Closure Compiler is fully capable of this. It appears that something is broken, don't know what.

In shadow-cljs [1] I handle JS dependencies quite differently so not only does this work there it is also much simpler (IMHO).

I made a demo showcasing all of the JS interop here:

You can clone it and run:
npm install
npx shadow-cljs watch app

These two files are interesting:

The CLJS files is using JS and the JS file is using CLJS. 100% full interop.

Note that the use of :default in the ns :require is not yet official [2].
The support for relative require (ie. "./foo") was rejected and is "never, ever going to happen." [3].

I consider this an experiment to explore alternatives to :foreign-libs as I think they are deeply flawed and should be abandoned.

It works well in shadow-cljs but please don't use it in any library until there is something "official" to make all of this work.

Cheers,
/thomas


Zalan Kemenczy

unread,
Nov 20, 2017, 11:38:04 AM11/20/17
to ClojureScript
Thomas, thanks for the detailed example!

I was reading over the weekend about shadow-cljs and it is definitely an intriguing approach. However, my motivation for these es6 experiments is to eventually bring them into a larger project I am working on, which already has an existing boot build framework. Realistically, I'm probably going to have to make it work within the boot ecosystem.

I'm curious why you think :foreign-libs is deeply flawed. It is my understanding that :npm-deps are also converted to :foreign-libs under the hood, and it seems like a lot of work has been put into these features over the last year or two. If the approach was deeply flawed, this would seem like a big deal.

Thomas Heller

unread,
Nov 20, 2017, 2:43:13 PM11/20/17
to ClojureScript
IMHO :foreign-libs do too many things these days and its not clear what to use when or how. This feature is alpha so I expect that everything will be sorted out properly in the future. I fully expect the "flaws" to be addressed.

:foreign-libs used to refer to "foreign JS" that was not processed in any way and just got prepended which was a great solution at the time and had a gigantic impact overall. It has some scaling issues but overall it works well.

The biggest flaw I saw was related to mixing :foreign-libs and :npm-deps. One library may be using cljsjs.react (aka foreign lib) while a newer one maybe using "react" via :npm-deps. This would lead to a situation where you had 2 React instances in your page. Since everything also happens at the classpath level there was no proper way to "configure" it besides moving/deleting your node_modules folder which I think is pretty odd. Again: this will probably be fixed.

I didn't like some of the decisions that were made and decided to build something on my own to address the issues I saw in my own code/project. YMMV.

> The CLJS file is using JS and the JS file is using CLJS. 100% full interop.

This is my goal and I'm close to achieving it.

Cheers,
/thomas

PS: Someone started some boot related work started recently:

PPS: I wrote some posts about my motivations and the implementation details in case you are interested.
Reply all
Reply to author
Forward
0 new messages