<< Back to previous view

[CLJS-2792] CraftyJS NPM dependency cannot be imported Created: 25/Jun/18  Updated: 22/Jan/19

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Marty Glaubitz Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: clojurescript, npm-deps

Operation System: Windows 10
Leiningen Version: Leiningen 2.8.1
Java Version: Java 1.8.0_60 Java HotSpot(TM) 64-Bit Server VM
ClojureScript Version: 1.10.238


I'm using ClojureScript with Figwheel and trying to use CraftyJs in ClojureScript.
This is my project.clj

(defproject my_project "0.1.0-SNAPSHOT"
:description "FIXME: write this!"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}

:min-lein-version "2.7.1"

:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.238"]
[org.clojure/core.async "0.4.474"]]

:plugins [[lein-figwheel "0.5.16"]
[lein-cljsbuild "1.1.7" :exclusions [[org.clojure/clojure]]]]

:source-paths ["src"]

:cljsbuild {:builds
[{:id "dev"
:source-paths ["src"]

;; The presence of a :figwheel configuration here
;; will cause figwheel to inject the figwheel client
;; into your build
:figwheel {:on-jsload "my_project.core/on-js-reload"
;; :open-urls will pop open your application
;; in the default browser once Figwheel has
;; started and compiled your application.
;; Comment this out once it no longer serves you.
:open-urls ["http://localhost:3449/index.html"]}

:compiler {:main my_project.core
:asset-path "js/compiled/out"
:install-deps true
:npm-deps {:craftyjs "0.8.0"}
:output-to "resources/public/js/compiled/my_project.js"
:output-dir "resources/public/js/compiled/out"
:source-map-timestamp true
;; To console.log CLJS data-structures make sure you enable devtools in Chrome
;; https://github.com/binaryage/cljs-devtools
:preloads [devtools.preload]}}
;; This next build is a compressed minified build for
;; production. You can build this with:
;; lein cljsbuild once min
{:id "min"
:source-paths ["src"]
:compiler {:output-to "resources/public/js/compiled/my_project.js"
:main my_project.core
:optimizations :advanced
:pretty-print false}}]}

:figwheel {;; :http-server-root "public" ;; default and assumes "resources" ;; :server-port 3449 ;; default ;; :server-ip "" :css-dirs ["resources/public/css"] ;; watch and update CSS ;; Start an nREPL server into the running figwheel process ;; :nrepl-port 7888 ;; Server Ring Handler (optional) ;; if you want to embed a ring handler into the figwheel http-kit ;; server, this is for simple ring servers, if this ;; doesn't work for you just run your own server :) (see lein-ring) ;; :ring-handler hello_world.server/handler ;; To be able to open files in your editor from the heads up display ;; you will need to put a script on your path. ;; that script will have to take a file path and a line number ;; ie. in ~/bin/myfile-opener ;; #! /bin/sh ;; emacsclient -n +$2 $1 ;; ;; :open-file-command "myfile-opener" ;; if you are using emacsclient you can just use ;; :open-file-command "emacsclient" ;; if you want to disable the REPL ;; :repl false ;; to configure a different figwheel logfile path ;; :server-logfile "tmp/logs/figwheel-logfile.log" ;; to pipe all the output to the repl ;; :server-logfile false }

;; Setting up nREPL for Figwheel and ClojureScript dev
;; Please see:
;; https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl
:profiles {:dev {:dependencies [[binaryage/devtools "0.9.9"]
[figwheel-sidecar "0.5.16"]
[cider/piggieback "0.3.1"]]
;; need to add dev source path here to get user.clj loaded
:source-paths ["src" "dev"]
;; for CIDER
;; :plugins [[cider/cider-nrepl "0.12.0"]]
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
;; need to add the compliled assets to the :clean-targets
:clean-targets ^{:protect false} ["resources/public/js/compiled"

However when running lein figwheel i see following in the console:

Compiling build :dev to "resources/public/js/compiled/my_project.js" from ["src"]...

SyntaxError: missing ) after argument list
at createScript (vm.js:74:10)
at Object.runInThisContext (vm.js:116:10)
at Object.<anonymous> ([eval]-wrapper:6:22)
at Module._compile (module.js:624:30)
at evalScript (bootstrap_node.js:480:27)
at startup (bootstrap_node.js:177:9)
at bootstrap_node.js:626:3

Successfully compiled build :dev to "resources/public/js/compiled/my_project.js" in 19.529 seconds.
and i can't import the library from my ClojureScript, i also see this:

Uncaught Error: Undefined nameToPath for craftyjs
at visitNode (base.js:1357)
at Object.goog.writeScripts_ (base.js:1369)
at Object.goog.require [as require_figwheel_backup_] (base.js:706)
at index.html:14
I already tried to manually delete the compiled JS output folder

Comment by Mike Fikes [ 30/Jun/18 7:05 PM ]

Hey Marty,

I think you can't directly use CraftyJS as an NPM dependency with ClojureScript. If you look on the CraftyJS website it shows additionally using Browserify.

Here is what happens if you try to use it as an NPM dep using minimal tooling:

{:deps {org.clojure/clojurescript {:mvn/version "1.10.339"}}}
{:npm-deps {:craftyjs "0.8.0"}
 :install-deps true}
$ clj -m cljs.main -co compiler-opts.edn -r
ClojureScript 1.10.339
cljs.user=> (require 'craftyjs)
      throw er; // Unhandled 'error' event

Error: Can't resolve 'fs' in '/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/craftyjs/src/graphics'
    at onError (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/Resolver.js:61:15)
    at loggingCallbackWrapper (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
    at runAfter (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/Resolver.js:158:4)
    at innerCallback (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/Resolver.js:146:3)
    at loggingCallbackWrapper (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
    at next (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/tapable/lib/Tapable.js:252:11)
    at innerCallback (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/Resolver.js:144:11)
    at loggingCallbackWrapper (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
    at next (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/tapable/lib/Tapable.js:249:35)
    at resolver.doResolve.createInnerCallback (/Users/mfikes/Desktop/CLJS-2792-npm-deps/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:44:6)

Error: goog.require could not find: craftyjs
	 require (clojure/browser/repl.cljs:226:33)

If you look at the CraftyJS code, it has a require('fs') call (which Browserify evidently helps sidestep.)

But, you can easy use CraftyJS as a foreign lib. To do so, set your compiler options instead to be:

{:foreign-libs [{:file "https://github.com/craftyjs/Crafty/releases/download/0.8.0/crafty.js"
                 :provides ["craftyjs"]}]}

Then, you can drive CraftyJS right from the REPL:

$ clj -m cljs.main -co compiler-opts.edn -r
ClojureScript 1.10.339
cljs.user=> (set! (.-innerHTML (.getElementById js/document "app")) "")
cljs.user=> (require 'craftyjs)

cljs.user=> (.init js/Crafty)
cljs.user=> (def player (-> js/Crafty
(.e "2D, Canvas, Color, Fourway")
(.attr #js {:x 100 :y 100 :w 50 :h 50})
(.color "blue")
(.fourway 3)))

After the above, you can control the box using your arrow keys.

Comment by Marty Glaubitz [ 01/Jul/18 9:58 AM ]

Thanks for the tip! But are you sure that

:file "https://github.com/craftyjs/Crafty/releases/download/0.8.0/crafty.js"

is supposed to work? On my windows machine i can only pass a local file path there...

Comment by Mike Fikes [ 01/Jul/18 1:55 PM ]

Hi Marty. Yes :file can be a URL. See https://clojurescript.org/reference/compiler-options#foreign-libs

Comment by Timothy Pratley [ 22/Jan/19 4:45 PM ]

Is this something that could be supported in the future?

Doing `npm install craftyjs --save` creates node_modules/craftyjs/src/crafty.js which is suitable for being used as the foreign-lib (its the same as the file at the end of the url). So it seems in principle that it would be convenient to be able to say "Get me <x> from node, and treat dist/y as a foreign-lib.

Which makes me wonder what :npm-deps currently does... presumably it is using the source of the package rather than the dist. That makes sense to me for things that don't use browserify (or intermediary build tools). But there are useful packages that do use browserify and these leave us to relying on a URL to a file or some manual steps to get the package and build it first. Would it be possible to specify a dependency that should be fetched, built and use a dist file instead of src? The benefit would be that we could use both styles of npm dependencies via the same mechanism.

It might need to have a different name like :npm-libs because I think it would need more than just a version... ie something like :npm-libs {"asciidoctor.js" {:version "1.5.9", :lib "dist/browser/asciidoctor.js"}}

I arrived at this thread trying to use asciidoctor.js which uses Browserify to build node_modules/asciidoctor.js/dist/browser/asciidoctor.js. The asciidoctor.js package is interesting because it targets both NodeJS and the browser, producing two output files [which also interestingly still require other stuff, but the browser doesn't require things like `fs`].

Generated at Fri Apr 19 21:23:07 CDT 2019 using JIRA 4.4#649-r158309.