GithubHelp home page GithubHelp logo

thheller / shadow-cljs Goto Github PK

View Code? Open in Web Editor NEW
2.2K 41.0 171.0 7.99 MB

ClojureScript compilation made easy

Home Page: https://github.com/thheller/shadow-cljs

License: Eclipse Public License 1.0

Clojure 87.85% Java 5.56% JavaScript 5.04% HTML 0.24% Shell 0.04% CSS 1.28%
clojurescript cljs hot-reload repl

shadow-cljs's Introduction

npm Clojars Project

shadow-cljs provides everything you need to compile your ClojureScript code with a focus on simplicity and ease of use.

Features

  • Good configuration defaults so you don't have to sweat the details
  • Seamless npm integration
  • Fast builds, reliable caching, ...
  • Supporting various targets :browser, :node-script, :npm-module, :react-native, :chrome-extension, ...
  • Live Reload (CLJS + CSS)
  • CLJS REPL
  • Code splitting (via :modules)

overview-img

Requirements

  • node.js (v6.0.0+, most recent version preferred)
  • npm (comes bundled with node.js) or yarn
  • Java SDK (Version 11+, Latest LTS Version recommended)

Quick Start

Creating your project can be done quickly using the npx create-cljs-project utility. npx is part of npm and lets us run utility scripts quickly without worrying about installing them first. The installer will create a basic project scaffold and install the latest version of shadow-cljs in the project.

$ npx create-cljs-project acme-app
npx: installed 1 in 5.887s
shadow-cljs - creating project: .../acme-app
Creating: .../acme-app/package.json
Creating: .../acme-app/shadow-cljs.edn
Creating: .../acme-app/.gitignore
Creating: .../acme-app/src/main
Creating: .../acme-app/src/test
----
Installing shadow-cljs in project.

npm notice created a lockfile as package-lock.json. You should commit this file.
+ shadow-cljs@<version>
added 88 packages from 103 contributors and audited 636 packages in 6.287s
found 0 vulnerabilities

----
Done.
----

The resulting project has the following structure

.
├── node_modules (omitted ...)
├── package.json
├── package-lock.json
├── shadow-cljs.edn
└── src
    ├── main
    └── test

shadow-cljs.edn will be used to configure your CLJS builds and CLJS dependencies. package.json is used by npm to manage JS dependencies.

Everything is ready to go if you just want to start playing with a REPL

$ npx shadow-cljs node-repl
# or
$ npx shadow-cljs browser-repl

When building actual projects we need to configure the build first and create at least one source file.

The default source paths are configured to use src/main as the primary source directory. It is recommended to follow the Java Naming Conventions to organize your CLJS namespaces. It is recommended to start all namespaces with a unique enough prefix (eg. company name, project name) to avoid conflicts with generic names such as app.core. Suppose you were building a Web Frontend for Acme Inc. using acme.frontend.app might be a good starting point as it can easily grow to include acme.backend.* later on.

Using the above example the expected filename for acme.frontend.app is src/main/acme/frontend/app.cljs.

Lets start with a simple example for a browser-based build.

(ns acme.frontend.app)

(defn init []
  (println "Hello World"))

Inside the shadow-cljs.edn :builds section add

{...
 :builds
 {:frontend
  {:target :browser
   :modules {:main {:init-fn acme.frontend.app/init}}
   }}}

This config tells the compiler to call (acme.frontend.app/init) when the generated JS code is loaded. Since no :output-dir is configured the default public/js is used. You can start the development process by running:

$ npx shadow-cljs watch frontend
...
a few moments later ...
...
[:frontend] Build completed. (134 files, 35 compiled, 0 warnings, 5.80s)

The compilation will create the public/js/main.js we configured above (:main becomes main.js in the :output-dir). Since we want to load this in the browser we need to create a HTML file in public/index.html.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>acme frontend</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="/js/main.js"></script>
  </body>
</html>

We also need a simple HTTP server to serve our HTML since modern Browsers all place a few restrictions on files loaded directly from disk which will lead to issues later. shadow-cljs provides such a server but you can use anything you like at this point. It only matters that the files from the public directory are served properly. To start the built-in web server just adjust the build config from above.

{...
 :dev-http {8080 "public"}
 :builds
 {:frontend
  {:target :browser
   :modules {:main {:init-fn acme.frontend.app/init}}
   }}}

Once the config is saved the server will automatically start and serve the content at http://localhost:8080. There is no need to restart shadow-cljs. When opening the above URL the Browser Console should show "Hello World".

To be continued ...

Documentation

Please refer to the User Manual. (Work in Progress)

Video Courses

Guides

Examples

Libraries

  • flexsurfer/rn-shadow-steroid - React Native with shadow-cljs on steroids
  • re-frame-flow - A graph based visualization tool for re-frame event chains using shadow-cljs
  • ... please let me know if you created something to include here

License

Copyright © 2022 Thomas Heller

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

shadow-cljs's People

Contributors

alex-dixon avatar arichiardi avatar awkay avatar baskeboler avatar djblue avatar ertugrulcetin avatar flexsurfer avatar ikappaki avatar iku000888 avatar jacekschae avatar laurio avatar lins05 avatar lucywang000 avatar luke1298 avatar mdhaney avatar olical avatar olimsaidov avatar palfrey avatar patbrown avatar prestancedesign avatar quangv avatar samuelwagen avatar shaunlebron avatar stanislas avatar stigi avatar sumbach avatar superstructor avatar the-alchemist avatar thheller avatar tiye avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shadow-cljs's Issues

aleph error when shutting down a worker while a REPL client is connected

Called .stop on the aleph instance.

End up with this if there is still an open websocket connection. It is only a log message but still annoying .. and marked as SEVERE.

Apr 05, 2017 2:24:27 PM clojure.tools.logging$eval20437$fn__20441 invoke
SEVERE: error in deferred handler
java.util.concurrent.RejectedExecutionException: Executor is shutdown!
	at io.aleph.dirigiste.Executor.execute(Executor.java:300)
	at manifold.deferred.Deferred$fn__21268.invoke(deferred.clj:395)
	at manifold.deferred.Deferred.success(deferred.clj:395)
	at manifold.deferred$success_BANG_.invokeStatic(deferred.clj:243)
	at manifold.deferred$success_BANG_.invoke(deferred.clj:240)
	at manifold.stream.default.Stream$fn__22883.invoke(default.clj:81)
	at manifold.stream.default.Stream.close(default.clj:80)
	at manifold.stream.BufferedStream.close(stream.clj:863)
	at aleph.http.server$websocket_server_handler$reify__26442.channelInactive(server.clj:490)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:245)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:231)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:224)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelInactive(ChannelInboundHandlerAdapter.java:75)
	at io.netty.handler.codec.MessageAggregator.channelInactive(MessageAggregator.java:406)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:245)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:231)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:224)
	at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:360)
	at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:325)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:245)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:231)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:224)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelInactive(DefaultChannelPipeline.java:1329)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:245)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:231)
	at io.netty.channel.DefaultChannelPipeline.fireChannelInactive(DefaultChannelPipeline.java:908)
	at io.netty.channel.AbstractChannel$AbstractUnsafe$7.run(AbstractChannel.java:744)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasksFrom(SingleThreadEventExecutor.java:379)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:354)
	at io.netty.util.concurrent.SingleThreadEventExecutor.confirmShutdown(SingleThreadEventExecutor.java:678)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:452)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
	at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
	at java.lang.Thread.run(Thread.java:745)

Apr 05, 2017 2:24:27 PM clojure.tools.logging$eval20437$fn__20441 invoke
SEVERE: error in deferred handler
java.util.concurrent.RejectedExecutionException: Executor is shutdown!
	at io.aleph.dirigiste.Executor.execute(Executor.java:300)
	at manifold.deferred.Deferred$fn__21268.invoke(deferred.clj:395)
	at manifold.deferred.Deferred.success(deferred.clj:395)
	at manifold.deferred$success_BANG_.invokeStatic(deferred.clj:243)
	at manifold.deferred$success_BANG_.invoke(deferred.clj:240)
	at manifold.stream.async.CoreAsyncSource$f__23348$fn__23349.invoke(async.clj:44)
	at clojure.core.async.impl.channels.ManyToManyChannel$fn__2554$fn__2555.invoke(channels.clj:95)
	at clojure.lang.AFn.run(AFn.java:22)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

Properly test new `ns` form parser

I wrote a completely new parser for the ns form based on clojure.spec.alpha. I can compile my work project with it already but it may choke on other code out there.

It will be much more strict since it is based on spec, I suspect there will be issues.

Error running cli: `Cannot find anything to run for: shadow.cljs.devtools.cli/once`

I'm trying to play around with the project. I get the following error when attempting to run devtools cli:

Exception in thread "main" java.lang.Exception: Cannot find anything to run for: shadow.cljs.devtools.cli/once, compiling:(/tmp/form-init2146566662431793486.clj:1:73)
        at clojure.lang.Compiler.load(Compiler.java:7469)
        at clojure.lang.Compiler.loadFile(Compiler.java:7395)
        at clojure.main$load_script.invokeStatic(main.clj:277)
        at clojure.main$init_opt.invokeStatic(main.clj:279)
        at clojure.main$init_opt.invoke(main.clj:279)
        at clojure.main$initialize.invokeStatic(main.clj:310)
        at clojure.main$null_opt.invokeStatic(main.clj:344)
        at clojure.main$null_opt.invoke(main.clj:341)
        at clojure.main$main.invokeStatic(main.clj:423)
        at clojure.main$main.doInvoke(main.clj:386)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:702)
        at clojure.main.main(main.java:37)
Caused by: java.lang.Exception: Cannot find anything to run for: shadow.cljs.devtools.cli/once
        at user$eval137.invokeStatic(form-init2146566662431793486.clj:1)
        at user$eval137.invoke(form-init2146566662431793486.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:7005)
        at clojure.lang.Compiler.eval(Compiler.java:6995)
        at clojure.lang.Compiler.load(Compiler.java:7457)
        ... 12 more

This is the command I ran:

lein run -m shadow.cljs.devtools.cli/once app

Here are the steps I took, I started from a fresh line template:

lein new app cl

edit project.clj to look like:

(defproject cl "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies
  [[org.clojure/clojure "1.9.0-alpha17"]
  [thheller/shadow-devtools "1.0.20170516"]]
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Create shadow-cljs.edn file:

[{
  :id :app
  :target :browser
  :public-dir "public/js"
  :public-path "/js"
  :modules
  {:main {:entries [cl.core]}}
}]

Rename src/cl/core.clj => src/cl/core.cljs

with these contents:

(ns cl.core)
(defn hello [] "hi there")

Add resources/public/index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <script src="/js/main.js"></script>
  </body>
</html>

run:

lein run -m shadow.cljs.devtools.cli/once app

If I run the command with just the namespace, then I get the help output:

$ lein run -m shadow.cljs.devtools.cli
Please use specify a build or use --npm
Command line args:
-----
  -b, --build BUILD-ID             use build defined in shadow-cljs.edn
      --dev                        compile once and watch
      --once                       compile once and exit
      --release                    compile in release mode and exit
      --debug                      debug mode, useful in combo with --release (pseudo-names, source-map)
      --npm                        run in npm compatibility mode
      --runtime TARGET      :node  (npm-only) node or browser
  -e, --entries NAMESPACES         (npm-only) comma seperated list of CLJS entry namespaces
  -v, --verbose
  -h, --help
-----

Make :after-load detect Promise return

Hey @thheller!

Following up on our Slack conversation, it would be cool if :after-load in :devtools could detect when a Promised is returned and wait for its fulfilment before doing its thing.

This could be an improvement for Node.js code and I was wondering what folks think about it.

refactor CLI args

I'm thinking about refactoring the CLI options so that you can run multiple commands with one go.

Something that would reduce left to right maybe.

shadow-cljs --once a --start b --start c --repl c
  • build :a once
  • start dev mode for :b
  • start dev mode for :c
  • connect to repl for :c

Currently this would require running shadow-cljs 4 times. With --server this doesn't matter as much but I think it would be useful to be able to start multiple things at once.

node-jre fails to run shadow-cljs

ZipException: error in opening zip file
	java.util.zip.ZipFile.open (ZipFile.java:-2)
	java.util.zip.ZipFile.<init> (ZipFile.java:219)
	java.util.zip.ZipFile.<init> (ZipFile.java:149)
	java.util.jar.JarFile.<init> (JarFile.java:166)
	java.util.jar.JarFile.<init> (JarFile.java:130)
	shadow.cljs.build/create-jar-manifest (build.clj:292)
	shadow.cljs.build/create-jar-manifest (build.clj:287)
	shadow.cljs.build/find-jar-resources (build.clj:489)
	shadow.cljs.build/find-jar-resources (build.clj:456)
	shadow.cljs.build/do-find-resources-in-path (build.clj:1270)
	shadow.cljs.build/do-find-resources-in-path (build.clj:1267)
	shadow.cljs.build/merge-resources-in-path (build.clj:1287)

No clue why.

Option to ignore :foreign-libs

(ns om.dom
  (:refer-clojure :exclude [map mask meta time select])
  (:require-macros [om.dom :as dom])
  (:require [cljsjs.react]
            [cljsjs.react.dom]
            [om.util :as util]
            [goog.object :as gobj]))

Any CLJS library may be using one or more CLJSJS package which will bring in :foreign-libs which are pre-packaged versions of npm packages specifically created for CLJS apps. See https://github.com/cljsjs/packages.

The npm support in shadow-cljs already provides direct npm interop and this isn't required anymore. Until CLJS properly supports (:require ["react" :as react]) there should be an option which just uses the externs from :foreign-libs but not the code.

Only challenge beyond that is that cljsjs.react and others do not provide anything but instead just ensure that the global React object exists and can be used.

The webpack integration must account for that and expose the global as well somehow.

[discuss] deploy cljs code on npm

Weeks ago I mentioned for js developers we put compiled js code in npm, and was told it has problems if the compiler is in a different version.

Now I think maybe we can put ClojureScript source code there, as long as the bundler recognize it. Suppose we have a project containing these entries:

package.json
src/
   example/
          main.cljs

We will need an entry in package.json that specifies:

  "clojure-package": true,
  "source-paths": ["src/"],

Good parts:

  • js developers are familiar with npm. Releasing a ClojureScript package is very easy
  • besides, some cljs developers(well, like me) would not need Lein or Boot anymore

Bad parts:

  • the compiler would become complicated in order to resolve a new package format
  • Clojure community already have Maven and Clojars, well...
  • if other tools do not follow, people still need Clojars to share packages

I'm not sure about the implementations. Maybe it's also a bad idea on managing classpaths.


update: There's a case, nested dependencies would be installed in a nested node_modules/ folder, which would make it hard for deciding classpaths https://docs.npmjs.com/how-npm-works/npm3

Only start a build if package.json or project.clj exist

shadow-cljs can be executed in any directory and will immediately compile cljs.core even if no other source file exist.

It probably shouldn't do anything if no source files are found or insist on either a package.json or project.clj file.

"shadow-cljs --help" asks got a config file in 0.9.5

I thought it would just print the help page?

=>> ./node_modules/.bin/shadow-cljs --help
shadow-cljs - missing configuration file
- /Users/chen/repo/respo/respo-examples/shadow-cljs.edn
Create one? [y/n]: n
=>>

I was trying to see if I can use an option to specify the location of the configuration file. In a strange scenario I wanted to share one config file in several sub-folders.

building warning is not obvious

With configs like:

{:id :dev
  :target :browser
  :modules {:main-dev {:entries [client.main]}}
  :public-dir "dist/dev/"
  :public-path "/dev/"
  :devtools {:after-load client.main/on-jsload!}}

Can read real warnings from the message:

image

Clojure(Script) Language Server

I would like to start an open discussion about tool support for CLJ(S).

Motivation

I started writing a Language Server Protocol (LSP) implementation [1] [2] for CLJ(S). I have started to implement the basics and can now feed CLJS compiler warnings/errors back to an editor. This uses the textDocument/didOpen|didSave notifications sent by the editor. The server will attempt to compile the mentioned files and provide feedback (via publishDiagnostics) back to the editor. This allows the server to completely skip watching the filesystem as a bonus and lets the editor display warnings/errors without knowing anything about CLJ(S) at all.

screen shot 2017-03-15 at 23 51 31

Obviously this isn't very useful without editors that support this protocol. The above screenshot is taken in Visual Studio Code since it supports the LSP out of the box. It does not however support nREPL or any other REPL for that matter. Fundamentally the requirements of tools are represented better by a dedicated protocol anyways than just a REPL. Long discussions [3] about this have happened in the past. One point made by Rich Hickey stands out to me:

But, we now have nice editors/IDEs often running on the same machine, with the ability to use sockets. Multiplexing their needs with those of a human REPL consumer over the same connection is going to make things bad for one or both of them.

nREPL has become the de-facto standard for tools to communicate with the runtime, whether they actually multiplex everything over one connection or use multiple connections I don't know. At this point I'm strictly talking about tooling related things the LSP would cover. This includes code completions, code navigation, formatting, documentation lookups, etc. It does not cover a REPL and shouldn't since that has very different needs than tools have (as mentioned above).

nREPL vs JSON-RPC

Assuming for now that the editor will connect to a dedicated TCP port with a dedicated protocol. The protocol is about framing messages between the LSP and the editor. This must support request/response semantics as well as simple fire-and-forget notifications in both directions.

  • LSP uses JSON-RPC as the transport encoding. Messages follow {:method string? :params any? :id num?} for requests and the same without :id for notifications. The other side must respond with {:id num-from-request} and a :result or :error entry to requests, notifications do not require a response.

  • nREPL uses bencode as the transport encoding. The only mandatory key on a message is :op. I'm not entirely sure about the semantics after that. Some middleware may clone a new session and then add a session and/or id to every message, others just seem to send a single message. I do not know how the server would make a request to the client.

nREPL or JSON-RPC?

The question now is which protocol we (the Clojure community) should use. Most tools already support nREPL but not LSP. There is growing support for LSP [4] [5] and using this protocol enables talking to other languages as well.

Please note that this has absolutely nothing to do with a REPL, this is strictly about editors communicating with the server and which protocol to use.

Adding additional :op or :method messages can be achieved in both and I already want to add things like cljs/list-builds cljs/start-worker {:build-id :website :autobuild true} and more.

References

[1] https://github.com/Microsoft/language-server-protocol

[2] https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md

[3] https://groups.google.com/d/topic/clojure-dev/Dl3Stw5iRVA/discussion

[4] https://github.com/Microsoft/language-server-protocol/wiki/Protocol-Implementations#editors-ides-supporting-the-protocol

[5] http://langserver.org/

Any chance to make use of Webpack tree shaking?

https://webpack.js.org/guides/tree-shaking/

Tree shaking is a term commonly used in the JavaScript context for dead-code elimination, or more precisely, live-code import. It relies on ES2015 module import/export for the static structure of its module system.

// This function isn't used anywhere
export function square(x) {
    return x * x;
}

// This function gets included
export function cube(x) {
    return x * x * x;
}

Related article: http://jakewiesler.com/tree-shaking-es6-modules-in-webpack-2/

Downside is ES2015 module is not supported by mainstream, then transpilers are required. I guess it would also be huge work to make it work Closure Compiler code.

Now cljs begins to support npm modules, however, most npm modules are written in CommonJS which does not support DCE, which means DCE powered by Closure Compiler is not that powerful to these modules.

Support :exclusions for package.json dependencies

Like in lein it should be supported to exclude certain dependencies

"dependencies: [
  ["dependency", "version", { "exclusions": ["foo" "bar"] }]
]

Maybe just flat as in lein without the extra "map".

Document --check

--check is very useful for finding missing externs in any build.

CLOSURE-WARNING: shadow/cljs/npm/cli.cljs[7:12] (compiled to shadow/cljs/npm/cli.js[8:79])
	Property statSync never defined on shadow.npm.fs

CLOSURE-WARNING: shadow/cljs/npm/cli.cljs[53:13] (compiled to shadow/cljs/npm/cli.js[97:165])
	Property existsSync never defined on shadow.npm.fs

CLOSURE-WARNING: shadow/cljs/npm/cli.cljs[54:4] (compiled to shadow/cljs/npm/cli.js[99:143])
	Property mkdirSync never defined on shadow.npm.fs

Used it to find all the :externs required for the shadow.cljs.npm.cli script.

Also useful since it does type checking but given that CLJS is mostly untyped it only finds errors in the Closure lib or when using typed :externs. Given that these are mostly incorrect --check may actually just confuse more than help.

Also should probably talk about how people are doing :externs wrong.

move configs out of package.json?

In writing these examples I more feel the configs stored in package.json is a bad idea.

Old and new reasons:

  • package.json is always formatted by yarn or npm, makes it hard to read
  • people tend to ignore configs inside here, it's not easy for editing too
  • dependencies written as [hsl "0.1.0"] is not valid JSON, need editing

It's also strange trying to explain the reason it's inside package.json. It's there a special reason?

compilation logs may be distracting

Currently I see logs:

image

Those logs are not useless, but when I changing code of my app, more the logs are useless. Informations I care about:

  • any compilation warnings(or errors)?
  • are the results written correctly?
  • what's current configurations during start?

Then most of the logs are distracting. Among them the compilation warnings are so important since it does not pop up tips like Figwheel does, I would rather it highlighted in yellow.


Plus, I see two same warnings here, anything wrong in my configs?

nREPL client mode

shadow-cljs currently starts a new JVM for each invocation.

Usually I have a JVM running with nREPL enabled during development. The CLI tool could connect to that and execute the command there without launching a JVM.

Could use .nrepl-port when found.

Could also add a shadow-cljs --server which could start its own nREPL server.

In warning: error, String index out of range: 77

------ WARNING #1 ------------------------------------------------------

 File: /Users/chen/repo/cirru/stack-editor/src/app/comp/graph.cljs:20:71
------------------------------------------------------------------------
  16 |  (div
  17 |   {:style (merge ui/fullscreen style-graph)}
  18 |   (div
  19 |    {:style style-toolbar}
  20 |    (button {:inner-text "Generate!", :style ui/button, :event {:click on-}}))
[:stdout-dump-ex #error {
 :cause "String index out of range: 77"
 :via
 [{:type java.lang.StringIndexOutOfBoundsException
   :message "String index out of range: 77"
   :at [java.lang.String substring "String.java" 1963]}]
 :trace
 [[java.lang.String substring "String.java" 1963]
  [clojure.core$subs invokeStatic "core.clj" 4913]
  [clojure.core$subs invoke "core.clj" 4907]
  [shadow.cljs.devtools.server.util$sep_line invokeStatic "util.clj" 58]
  [shadow.cljs.devtools.server.util$sep_line invoke "util.clj" 57]
  [shadow.cljs.devtools.server.util$print_warning invokeStatic "util.clj" 97]
  [shadow.cljs.devtools.server.util$print_warning invoke "util.clj" 74]
  [shadow.cljs.devtools.server.util$print_build_complete invokeStatic "util.clj" 130]
  [shadow.cljs.devtools.server.util$print_build_complete invoke "util.clj" 107]
  [shadow.cljs.devtools.server.util$print_worker_out invokeStatic "util.clj" 158]
  [shadow.cljs.devtools.server.util$print_worker_out invoke "util.clj" 138]
  [shadow.cljs.devtools.server.util$stdout_dump$fn__15622$state_machine__5358__auto____15627$fn__15630 invoke "util.clj" 191]
  [shadow.cljs.devtools.server.util$stdout_dump$fn__15622$state_machine__5358__auto____15627 invoke "util.clj" 191]
  [clojure.core.async.impl.ioc_macros$run_state_machine invokeStatic "ioc_macros.clj" 973]
  [clojure.core.async.impl.ioc_macros$run_state_machine invoke "ioc_macros.clj" 972]
  [clojure.core.async.impl.ioc_macros$run_state_machine_wrapped invokeStatic "ioc_macros.clj" 977]
  [clojure.core.async.impl.ioc_macros$run_state_machine_wrapped invoke "ioc_macros.clj" 975]
  [clojure.core.async.impl.ioc_macros$take_BANG_$fn__5376 invoke "ioc_macros.clj" 986]
  [clojure.core.async.impl.channels.ManyToManyChannel$fn__433$fn__434 invoke "channels.clj" 95]
  [clojure.lang.AFn run "AFn.java" 22]
  [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1142]
  [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 617]
  [java.lang.Thread run "Thread.java" 745]]}]

My code:

(ns app.comp.graph
  (:require-macros (respo.macros :refer (defcomp)))
  (:require [hsl.core :refer [hsl]]
            [respo.alias :refer [div button]]
            (respo-ui.style :as ui)
            (respo.comp.text :refer (comp-text))))

(def style-graph {:background-color (hsl 0 0 0)})

(def style-toolbar {:padding 16})

(defcomp
 comp-graph
 (store)
 (div
  {:style (merge ui/fullscreen style-graph)}
  (div
   {:style style-toolbar}
   (button {:inner-text "Generate!", :style ui/button, :event {:click on-}}))
  (comp-text "Graph" nil)))

(defn on-load [dispatch! e] (dispatch! :graph/load-graph nil))

I think the problem is {:click on-}, where on- is broken since it's not defined.

internal server error for WebSockets

Never saw this error before. Tried, it happened in 0.9.6 and 0.9.7.

$ shadow-cljs -b dev --dev
shadow-cljs - 1.0.20170615 using /Users/chen/repo/cirru/stack-editor/shadow-cljs.edn
shadow-cljs - updating dependencies
shadow-cljs - dependencies updated
shadow-cljs - optimizing startup
shadow-cljs - starting ...
[:dev] Configuring build.
[:dev] Compiling ...
[:dev] Build completed. (194 files, 92 compiled, 0 warnings, 20.30s)
[0:0] cljs.user=> Jun 15, 2017 2:23:52 PM aleph.http.server invoke
SEVERE: error in HTTP handler
java.lang.NullPointerException
	at java.util.UUID.fromString(UUID.java:192)
	at shadow.cljs.devtools.server.worker.ws$process.invokeStatic(ws.clj:201)
	at shadow.cljs.devtools.server.worker.ws$process.invoke(ws.clj:189)
	at shadow.cljs.devtools.api$web_root.invokeStatic(api.clj:30)
	at shadow.cljs.devtools.api$web_root.invoke(api.clj:23)
	at shadow.cljs.devtools.api$get_ring_handler$fn__20083.invoke(api.clj:44)
	at aleph.http.server$handle_request$fn__17506$f__12968__auto____17507.invoke(server.clj:156)
	at clojure.lang.AFn.run(AFn.java:22)
	at io.aleph.dirigiste.Executor$Worker$1.run(Executor.java:62)
	at manifold.executor$thread_factory$reify__12860$f__12861.invoke(executor.clj:44)
	at clojure.lang.AFn.run(AFn.java:22)
	at java.lang.Thread.run(Thread.java:745)

image

part of my config, currently using -b dev

{
 :builds {:dev {:output-dir "target/browser"
                :asset-path "/browser"
                :target :browser
                :modules {:main {:entries [app.main]}}}
          :npm {:output-dir "target"
                :target :npm-module}
          :release {:entries [app.main]
                    :target :npm-module
                    :output-dir "target/release/"}}}

fix node-repl

node-repl is currently broken in some cases since everything is no longer global.

[21:0]~shadow.user=> (shadow/node-repl)
[21:1]~cljs.user=> (require 'demo.npm)
[21:1]~cljs.user=> demo.npm/foo
eval error ReferenceError: demo is not defined

Need to make sure that eval does the same things as require to ensure the proper env.

--server mode

Starting with [thheller/shadow-cljs "1.0.20170613"] and [email protected] I added a proper --server mode.

There are several things this is meant to solve

  • running builds in parallel
  • less annoying REPL
  • controlling builds remotely
  • possibly a nice Web UI (in the future)

By default it starts a HTTP Server at {:host "localhost" :port 8200} (configured via the :http key in the config). As of today this does nothing useful other than being the endpoint for the devtools websockets.

It also starts a Clojure Socket REPL at {:host "localhost" :port 8201} (configured via the :repl key in the config). This is not nREPL but a plain Clojure 1.8 Socket REPL which means anything that is able to connect to a TCP port can be used as a client. rlwrap nc localhost 8201 is the most basic example of such a client.

Running shadow-cljs --server looks something like this

shadow-cljs - 1.0.20170613 using /Users/zilence/code/shadow-cljs-examples/cljs-react-app/shadow-cljs.edn
shadow-cljs - updating dependencies
Retrieving thheller/shadow-cljs/1.0.20170613/shadow-cljs-1.0.20170613.pom from http://clojars.org/repo/
Retrieving thheller/shadow-cljs/1.0.20170613/shadow-cljs-1.0.20170613.jar from http://clojars.org/repo/
shadow-cljs - dependencies updated
shadow-cljs - optimizing startup
shadow-cljs - starting ...
shadow-cljs - server running at http://localhost:8200
shadow-cljs - socket repl running at tcp://localhost:8201

If you now connect to the Socket REPL via rlwrap nc localhost 8201 you should be greeted with some intro text and a REPL prompt

shadow-cljs - CLJ REPL

(shadow/start-worker :foo) - Starts a dev process for build :foo
(shadow/repl :foo) - Switches the current REPL to that of :foo
(shadow/stop-worker :foo) - Stop the dev process

(shadow/once :foo) - Compiles a dev build once
(shadow/release :foo) - Create a release build for :foo

More coming soon ...
[1:0] shadow.user=>

It is a normal Clojure REPL that provides the above utility functions but all Clojure works. To upgrade this REPL into a CLJS REPL you first need to start a worker by calling (shadow/start-worker :npm)

[1:0] shadow.user=> (shadow/start-worker :npm)
:started

All output generated by this worker is displayed in the window of the --server process as I think it is way too annoying to mix the REPL with build logs.

You can connect to the CLJS REPL whenever you need to by calling shadow/repl

[1:0] shadow.user=> (shadow/repl :npm)
[1:1] cljs.user=>

Your REPL is now upgraded to a CLJS REPL. Type :repl/quit to drop back down to the normal CLJS REPL. :repl/quit also exits the CLJ REPL and disconnects the socket. The --server will remain running.

There is one extra new config entry :server {:autostart #{:foo :bar}} which lets you autostart a few dev workers when starting the --server process as a convenience.

:module-loader true defeats purpose of :module-hash-names

:module-loader currently always appends the module config to the default module. By doing so it may change the md5 signature of the default module which in turn means we break the caching promise.

However only the module config changed, not the code. So the module config must be kept in a different place and not affect the signature/contents of the default module.

nREPL

I want to support nREPL since basically every tool uses nREPL. Cursive for example can only connect to a remote nREPL and not a Socket REPL (yet).

While developing I typically want a separate CLJ and CLJS REPL that don't interfere with each other. I may also be working on multiple CLJS builds at once where each needs its own CLJS REPL but they can share the CLJ one. One good example for this workflow are electron apps, you'd have one for the main app (:node-library) and one for the renderer (:browser).

I do not know how to best handle this in the UI side of things but for now I assume I have 3 different windows/tabs somewhere that I can switch between. One goes to the CLJ REPL and one for each CLJS REPL. Whatever I eval in the renderer CLJS should not affect the main in any way. The CLJ REPL can be used to control the CLJS build processes (ie. start/stop autobuild, test release build, launch cljs tests, etc).

I like verbose compilation output but it can get very annoying while working in a REPL and it constantly messing up the prompt. Ideally there would be an extra "log" section for each REPL window that shows the compiler output and detailed errors should a build fail.

The easiest path to get all this seems to be nREPL.

In order to achieve this there are 2 possible setups I can come up with:

  • separate nREPL server per build
  • special cljs/* commands that just use the default nREPL connection and provide a :build key that selects where commands should go.

Server per build

Isolated clojure.tools.nrepl.server instances with their own private nREPL middleware stack specifically configured for CLJS. The server only knows how to talk to one build. The setup here seems simpler but juggling ports quickly gets annoying.

cljs/* commands

This would basically just be a dedicated middleware injected into the main server. Extra care must be taken that all :op messages are targetted to a specific build. piggieback currently just hijacks eval when CLJS REPL is running so you cannot eval CLJ anymore. This sucks.

Adding commands is simple enough and this could be extended to allow tools to better work with the somewhat complex options CLJS builds may have.

  • {:op cljs/list-builds} -> [{:id :website :target :browser ...} ...] as a quick way to let the client lookup all configured builds.
  • {:op :cljs/start-worker :build :website :autobuild true} starts the development process for one of the configured builds.
  • {:op :cljs/sync :build :website} wait for compilation to finish
  • {:op :cljs/eval :build :website :code "(+ 1 1)"} eval the given code in the JS runtime connected to the build (TBD should be able to select which JS runtime is multiple are connected, ie multiple browser windows open for the same build)
  • {:op :cljs/stop-worker :build :website} stop the worker
  • {:op :cljs/once :build :website} compile dev build once (no REPL)
  • {:op :cljs/release :build :website} compile release build

Once one or more workers are started the nREPL server may start sending messages back to the client that weren't triggered by the client.

  • {:op :cljs/notify :build :website :type :build-log :message string?}
  • {:op :cljs/notify :build :website :type :build-start}
  • {:op :cljs/notify :build :website :type :build-complete :warnings [...]} build finished, no errors, maybe warnings (may include more info)
  • {:op :cljs/notify :build :website :type :build-failure ...} hard error that could not complete a build.

I'm not sure if it is worth capturing prn in the JS runtime. My logging in CLJS consists of (js/console.log ...) and nothing else but maybe there should also be a :out notify.

I started implementing some of it here:
https://github.com/thheller/shadow-devtools/blob/master/src/main/shadow/cljs/devtools/nrepl.clj

Now need to figure out if any editor would actually use this. This should also be something the figwheel and boot-cljs want/can implement so we can find and agree on a standard for this. My sketch is pretty specific for shadow-devtools but should translate to any other build tool as well.

goog.require('cljs.core') is emitted twice

var CLJS_ENV = require("./cljs_env");
var COMPILED = false;
require("./cljs.core.js");
var cljs=CLJS_ENV.cljs;
var goog=CLJS_ENV.goog;
var client=CLJS_ENV.client || (CLJS_ENV.client = {});
goog.provide('client.lib');
goog.require('cljs.core');
goog.require('cljs.core');
console.log("Loading lib.cljs");
client.lib.lib_data = "Edit lib.cljs to change 2!";

module.exports = client.lib;

It is a noop but should be fixed.

Interactive mode for CLI

The goals of --interactive are

  • quickly toggle between --dev --once --release --check for builds
  • run & monitor many builds in parallel
  • interactive test runner ala Jest?

The shadow-cljs CLI tool has a few issues when working with multiple builds in parallel. In my work project I typically have one build running for the the frontend website and one for the backend admin. Quite often a third for experiments is also running.

In :verbose mode these produce way too much output to follow what is going on in a single terminal. Even in without :verbose these can produce too much output. One particular issue are warnings. If a warning is found in one file that is shared by all builds it will be repeated for each build.

Can't have the output spread into multiple terminals since I can't look at all those all the time and may not notice a warning at all until the code stops working.

So the highest priority to display are build errors and then build warnings (de-duped if multiple builds are running). Maybe with some kind of source excerpt and color.

The plan is to have a shadow-cljs --interactive mode that will launch (or connect to) a JVM process and interact with it via a remote API. There should be shortcuts for starting --dev mode for particular builds (or all) and monitoring them. In the main view it should only show the status of all builds and errors/warnings. Build progress is not important here.

I don't like doing this as a REPL since the process must be able to show output at all times. Just printing is annoying because it messes with the REPL prompt and interleaving REPL output with build output is annoying as well.

The interactive mode could have a REPL option that switches to a REPL view and then suppresses the build output but I think the better solution is to have a REPL in a dedicated tag/window where build progress doesn't interfere with the REPL.

I started this as a web UI but wanted to see how far I would get with a terminal first. It may be better to use a web UI though since it is easier to show more information and a terminal is quite limited.

:advanced for webpack

The plan is to support :advanced for webpack by abusing the Closure JSModule support but instead of grouping many files into one JSModule each file gets its own module. Coupled with RescopeGlobalSymbols that produces something that can be understood by webpack while still being reasonably small and DCE'd.

JS side would remain as require("shadow-cljs/demo.foo").foo() but the CLJS side would need to add the :export meta to anything that should be accessible from the outside, ie. (defn ^:export foo [] ..).

webpack could then do the chunking as normal. The :browser style module definition could also be used so we could keep the grouping and expose less files to webpack. API remains the same.

the output looks something like this:

var window = global;
var $ = require("./cljs_env");
$.module = module;
require("./cljs.core.js");
$.Ie = new $.T("code-split.a", "a", "code-split.a/a", 1094652911);
window.console.log($.Ie, $.module);
$.module.exports = {
  foo: function() {
    return "foo";
  }
}

$ is the the Closure "global" and everything is rescoped to it. So basically instead of having cljs.core.assoc you'd have $.cljs.core.assoc which then is shortened to something like $.x.

var window = global; is needed because the Closure Compiler assumes that is the "true" global. Would need a patch to Closure as it is hard-coded. Faking its existence works though.

$.module = module; is required since closure will rewrite EVERYTHING that wants to use a global variable to window, so js/module becomes window.module not module. Again that would need a Closure patch to add the node.js implicit vars to their set of globals.

See: https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/RescopeGlobalSymbols.java#L57-L80

I can't figure out why this doesn't work though? Am I just missing something obvious?

$.module.exports = {
  foo: function() {
    return "foo";
  }
}

@jiyinyiyong any ideas? if I change $.module.exports to module.exports it works but I can't yet make the closure compiler emit that.

Edit: https://github.com/thheller/shadow-cljs/blob/a2200a3ad6eae4c2e579be306a367ab29bf53ecd/src/test/code_split/a.cljs this file produced the above output.

webpack reports "critical dependency" for cljs.nodejs.js

With latest shadow-cljs emitted code for Node.js , Webpack reports:

WARNING in ./compiled/cljs.nodejs.js
10:22-29 Critical dependency: require function is used in a way in which dependencies cannot be statically extracted

the code:

var CLJS_ENV = require("./cljs_env");
var COMPILED = false;
require("./cljs.core.js");
var cljs=CLJS_ENV.cljs;
var goog=CLJS_ENV.goog;

goog.provide('cljs.nodejs');
goog.require('cljs.core');
goog.require('cljs.core');
cljs.nodejs.require = require;
cljs.nodejs.process = process;
cljs.nodejs.enable_util_print_BANG_ = (function cljs$nodejs$enable_util_print_BANG_(){
cljs.core._STAR_print_newline_STAR_ = false;

cljs.core._STAR_print_fn_STAR_ = (function() { 
var G__547__delegate = function (args){
return console.log.apply(console,cljs.core.into_array.cljs$core$IFn$_invoke$arity$1(args));
};
var G__547 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__561__i = 0, G__561__a = new Array(arguments.length -  0);
while (G__561__i < G__561__a.length) {G__561__a[G__561__i] = arguments[G__561__i + 0]; ++G__561__i;}
  args = new cljs.core.IndexedSeq(G__561__a,0,null);
} 
return G__547__delegate.call(this,args);};
G__547.cljs$lang$maxFixedArity = 0;
G__547.cljs$lang$applyTo = (function (arglist__564){
var args = cljs.core.seq(arglist__564);
return G__547__delegate(args);
});
G__547.cljs$core$IFn$_invoke$arity$variadic = G__547__delegate;
return G__547;
})()
;

cljs.core._STAR_print_err_fn_STAR_ = (function() { 
var G__566__delegate = function (args){
return console.error.apply(console,cljs.core.into_array.cljs$core$IFn$_invoke$arity$1(args));
};
var G__566 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__571__i = 0, G__571__a = new Array(arguments.length -  0);
while (G__571__i < G__571__a.length) {G__571__a[G__571__i] = arguments[G__571__i + 0]; ++G__571__i;}
  args = new cljs.core.IndexedSeq(G__571__a,0,null);
} 
return G__566__delegate.call(this,args);};
G__566.cljs$lang$maxFixedArity = 0;
G__566.cljs$lang$applyTo = (function (arglist__572){
var args = cljs.core.seq(arglist__572);
return G__566__delegate(args);
});
G__566.cljs$core$IFn$_invoke$arity$variadic = G__566__delegate;
return G__566;
})()
;

return null;
});

module.exports = cljs.nodejs;

//# sourceMappingURL=...dropped...

Reproduce code https://github.com/Cumulo/cumulo-workflow/tree/master/server

yarn
yarn cljs
# new tty
yarn dev

optimize :npm-module flush

It currently writes all or nothing. It should not emit files that did not change, to lessen the burden on file-watchers.

shadow-cljs.edn Does not work

Trying to run $ shadow-cljs --once fails if shadow-cljs.edn exists. Even if using the default config from node_modules/shadow-cljs/bin/default-config.edn, the same error is produced:

$ shadow-cljs --once
shadow-cljs - using package.json 1.0.20170601
shadow-cljs - loading dependencies
shadow-cljs - starting
Exception in thread "main" clojure.lang.ExceptionInfo: invalid config {:clojure.spec.alpha/problems [{:path [], :pred clojure.core/vector?, :val {:source-paths ["src"], :dependencies [], :builds {}}, :via [:shadow.cljs.devtools.config/config], :in []}], :clojure.spec.alpha/spec :shadow.cljs.devtools.config/config, :clojure.spec.alpha/value {:source-paths ["src"], :dependencies [], :builds {}}}
	at clojure.core$ex_info.invokeStatic(core.clj:4725)
	at clojure.core$ex_info.invoke(core.clj:4725)
	at shadow.cljs.devtools.config$load_cljs_edn_BANG_.invokeStatic(config.clj:41)
	at shadow.cljs.devtools.config$load_cljs_edn_BANG_.invoke(config.clj:37)
	at shadow.cljs.devtools.config$get_build.invokeStatic(config.clj:47)
	at shadow.cljs.devtools.config$get_build.invoke(config.clj:45)
	at shadow.cljs.devtools.cli$load_npm_config.invokeStatic(cli.clj:69)
	at shadow.cljs.devtools.cli$load_npm_config.invoke(cli.clj:64)
	at shadow.cljs.devtools.cli$main.invokeStatic(cli.clj:124)
	at shadow.cljs.devtools.cli$main.doInvoke(cli.clj:100)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.core$apply.invoke(core.clj:652)
	at shadow.cljs.cli$_main.invokeStatic(cli.clj:77)
	at shadow.cljs.cli$_main.doInvoke(cli.clj:34)
	at clojure.lang.RestFn.applyTo(RestFn.java:139)
	at shadow.cljs.cli.main(Unknown Source)

The spec seems to be expecting a vector and not a map from shadow-cljs.edn. I'm assuming this is not the intended behavior.

Hi, what does this `file violation` mean?

shadow-cljs - using package.json 1.0.20170527
shadow-cljs - loading dependencies
shadow-cljs - starting
[:npm] Compiling ...
-> Finding resources in classpath
File violation: "wechat-reframe/config.cljs" produced unexpected ns "wechat-reframe.config"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/config.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/config.cljs
File violation: "wechat-reframe/events.cljs" produced unexpected ns "wechat-reframe.events"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/events.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/events.cljs
File violation: "wechat-reframe/views.cljs" produced unexpected ns "wechat-reframe.views"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/views.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/views.cljs
File violation: "wechat-reframe/core.cljs" produced unexpected ns "wechat-reframe.core"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/core.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/core.cljs
File violation: "wechat-reframe/subs.cljs" produced unexpected ns "wechat-reframe.subs"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/subs.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/subs.cljs
File violation: "wechat-reframe/db.cljs" produced unexpected ns "wechat-reframe.db"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/db.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/db.cljs
File violation: "wechat-reframe/component/search.cljs" produced unexpected ns "wechat-reframe.component.search"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/component/search.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/component/search.cljs
File violation: "wechat-reframe/routes.cljs" produced unexpected ns "wechat-reframe.routes"
Expected: /Users/yuzhao/gits/alpha-app/src/cljs/wechat_reframe/routes.cljs (or .cljc)
Provided: /Users/yuzhao/gits/alpha-app/src/cljs/wechat-reframe/routes.cljs
<- Finding resources in classpath (325 ms)
Compiling 12 sources (4 threads)
[CACHED] cljs/core.cljs
[CACHED] demo/foo.cljs
-> NPM module flush: /Users/yuzhao/gits/alpha-app/node_modules/shadow-cljs
<- NPM module flush: /Users/yuzhao/gits/alpha-app/node_modules/shadow-cljs (9 ms)
[:npm] Build completed. (12 files, 0 compiled, 0 warnings, 0.43s)

[RFC] Better support for importing non-closure code

This is an experiment for a new CLJS feature that would map npm packages to CLJS namespaces automatically.

Goal

Instead of writing this

(ns demo.browser)

(def react (js/require "react"))
(def rdom (js/require "react-dom"))
(def render (.-render rdom))
(def Component (.-Component react))

(defn foo []
  (.createElement react "h1" nil "hello from react"))

(render (foo) (js/document.getElementById "app"))

shadow-cljs now supports writing

(ns demo.browser
  (:require ["react" :as react :refer (Component)]
            ["react-dom/server" :as rdom :refer (render)]))

(defn foo []
  (react/createElement "h1" nil "hello from react"))

(render (foo) (js/document.getElementById "app"))

Implementation

This is achieved by creating a pseudo resource that mimics the namespace.

goog.provide("shadow.npm.react")

// for npm/node targets
shadow.npm.react = require("react");

// for browser targets
shadow.npm.react = window["npm$modules"]["react"];

For browser targets a tool like webpack must be used to properly create the
window["npm$modules"] data.

This can be as simple as creating a bundle.js and running webpack bundle.js dist/bundle.js and including the generated file before the CLJS compiled output.

var x = window["npm$modules"] = {};
x["react"] = require("react");
x["react-dom"] = require("react-dom");

The upside is that this is fully compatible with the Closure Compiler and webpack -p since they don't know about each other just like :foreign-libs.

Edits

  • removed the original idea of creating automatic aliases like npm.react based on feedback. There should not be special namespace magic.

Implement chokidar based file-watcher

The JVM FileWatcher is pretty slow on OSX (and probably other platforms as well). It takes about 2 seconds to be notified when a file changes. In practice that usually isn't a problem but can sometimes feel a bit sluggish.

Given that a --dev mode recompile is pretty fast (usually much less than 1s) it still makes the compiler feel slow compared to say webpack.

I don't want to write a custom file watcher that constantly checks all files since that requires way too much CPU time and just isn't worth it.

The shadow-cljs CLI tool however provides the opportunity to use the chokidar node library which works much better. The script currently uses spawnSync and does nothing while the JVM is running but that could be extended to use spawn and then watch the filesystem and report changes to the JVM process.

Currently trying to find good solutions on how the node.js process and JVM could communicate. stdio is piped through to the JVM for the REPL so that is not an option.

Current Plan

  • The node process should start a TCP/UDP/HTTP server and pass the port along to the JVM when starting.
  • The JVM process could then connect to that
  • Since it should be streaming HTTP might not be the best choice (also overkill)
  • TCP is probably the best using a simple EDN/Transit/JSON protocol
  • UDP?

confusions on generated files and folders

  • compiled js in node_modules/shadow-cljs occasionally missing.
    Perhaps yarn install cleared those files. I encountered it for several times.

  • unexpected new folders
    I saw .shadow-cljs/ target/shadow-cache/ target/shadow-build/ generated automatically. Why isn't it only one file?

I tried migrate two running projects written by `re-frame` and it shows the same error.

Hi, I tried migrate two running projects written by re-frame and it shows the same error: ×
TypeError: Cannot read property 'ReactComponentTreeHook' of undefined

I've been using re-frame-template to generate projects and it works well. When I try to migrate the cljs codes and import {init} from 'shadown-cljs/xxxx.core;' in the create-react-app 's index.js file, it shows the following error:
(before that I've add dependencies in package.json and have successfully run shadow-cljs --once)

×
TypeError: Cannot read property 'ReactComponentTreeHook' of undefined
Object.132
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:17021
  17018 | 
  17019 | var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
  17020 | 
> 17021 | module.exports = ReactInternals.ReactComponentTreeHook;
  17022 | },{}],133:[function(_dereq_,module,exports){
  17023 | /**
  17024 |  * Copyright 2013-present, Facebook, Inc.
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.50.132
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:8359
  8356 | 
  8357 | var ReactInvalidSetStateWarningHook = _dereq_(65);
  8358 | var ReactHostOperationHistoryHook = _dereq_(60);
> 8359 | var ReactComponentTreeHook = _dereq_(132);
  8360 | var ExecutionEnvironment = _dereq_(136);
  8361 | 
  8362 | var performanceNow = _dereq_(155);
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.64.50
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:9530
  9527 | var debugTool = null;
  9528 | 
  9529 | if ("development" !== 'production') {
> 9530 |   var ReactDebugTool = _dereq_(50);
  9531 |   debugTool = ReactDebugTool;
  9532 | }
  9533 | 
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.75.157
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:11479
  11476 | 'use strict';
  11477 | 
  11478 | var ReactRef = _dereq_(76);
> 11479 | var ReactInstrumentation = _dereq_(64);
  11480 | 
  11481 | var warning = _dereq_(157);
  11482 | 
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.82.100
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:12743
  12740 | var CallbackQueue = _dereq_(6);
  12741 | var PooledClass = _dereq_(25);
  12742 | var ReactFeatureFlags = _dereq_(58);
> 12743 | var ReactReconciler = _dereq_(75);
  12744 | var Transaction = _dereq_(100);
  12745 | 
  12746 | var invariant = _dereq_(150);
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.7.114
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:1014
  1011 | var EventPropagators = _dereq_(20);
  1012 | var ExecutionEnvironment = _dereq_(136);
  1013 | var ReactDOMComponentTree = _dereq_(34);
> 1014 | var ReactUpdates = _dereq_(82);
  1015 | var SyntheticEvent = _dereq_(91);
  1016 | 
  1017 | var getEventTarget = _dereq_(114);
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.52.1
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:8787
  8784 | 
  8785 | var ARIADOMPropertyConfig = _dereq_(1);
  8786 | var BeforeInputEventPlugin = _dereq_(3);
> 8787 | var ChangeEventPlugin = _dereq_(7);
  8788 | var DefaultEventPluginOrder = _dereq_(14);
  8789 | var EnterLeaveEventPlugin = _dereq_(15);
  8790 | var HTMLDOMPropertyConfig = _dereq_(22);
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.31.108
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:5363
  5360 | 'use strict';
  5361 | 
  5362 | var ReactDOMComponentTree = _dereq_(34);
> 5363 | var ReactDefaultInjection = _dereq_(52);
  5364 | var ReactMount = _dereq_(67);
  5365 | var ReactReconciler = _dereq_(75);
  5366 | var ReactUpdates = _dereq_(82);
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
Object.48.158
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:8213
  8210 | 
  8211 | var _assign = _dereq_(158);
  8212 | 
> 8213 | var ReactDOM = _dereq_(31);
  8214 | 
  8215 | var ReactDOMUMDEntry = _assign({
  8216 |   __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
View compiled
s
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
e
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
i
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:38
  35 |     f(g.React)
  36 |   }
  37 | })(function(React) {
> 38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
  41 |  * All rights reserved.
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:14
  11 | ;(function(f) {
  12 |   // CommonJS
  13 |   if (typeof exports === "object" && typeof module !== "undefined") {
> 14 |     f(require('react'));
  15 | 
  16 |   // RequireJS
  17 |   } else if (typeof define === "function" && define.amd) {
View compiled
(anonymous function)
node_modules/shadow-cljs/cljsjs.react-dom.development.react-dom.inc.js:37
  34 |     }
  35 |     f(g.React)
  36 |   }
> 37 | })(function(React) {
  38 |   (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ReactDOM = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  39 | /**
  40 |  * Copyright 2013-present, Facebook, Inc.
View compiled
__webpack_require__
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:659
  656 | };
  657 | 
  658 | // Execute the module function
> 659 | modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
  660 | 
  661 | // Flag the module as loaded
  662 | module.l = true;
View compiled
fn
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:85
  82 | 		console.warn("[HMR] unexpected require(" + request + ") from disposed module " + moduleId);
  83 | 		hotCurrentParents = [];
  84 | 	}
> 85 | 	return __webpack_require__(request);
  86 | };
  87 | var ObjectFactory = function ObjectFactory(name) {
  88 | 	return {
View compiled
(anonymous function)
node_modules/shadow-cljs/reagent.dom.js:9
   6 | require("./cljs.core.js");
   7 | require("./reagent.impl.template.js");
   8 | require("./reagent.impl.batching.js");
>  9 | require("./cljsjs.react-dom.development.react-dom.inc.js");
  10 | require("./reagent.debug.js");
  11 | var cljsjs=CLJS_ENV.cljsjs;
  12 | var cljs=CLJS_ENV.cljs;
View compiled
__webpack_require__
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:659
  656 | };
  657 | 
  658 | // Execute the module function
> 659 | modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
  660 | 
  661 | // Flag the module as loaded
  662 | module.l = true;
View compiled
fn
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:85
  82 | 		console.warn("[HMR] unexpected require(" + request + ") from disposed module " + moduleId);
  83 | 		hotCurrentParents = [];
  84 | 	}
> 85 | 	return __webpack_require__(request);
  86 | };
  87 | var ObjectFactory = function ObjectFactory(name) {
  88 | 	return {
View compiled
(anonymous function)
node_modules/shadow-cljs/reagent.core.js:11
   8 | require("./reagent.impl.batching.js");
   9 | require("./reagent.impl.component.js");
  10 | require("./reagent.debug.js");
> 11 | require("./reagent.dom.js");
  12 | var cljs=CLJS_ENV.cljs;
  13 | var goog=CLJS_ENV.goog;
  14 | var reagent=CLJS_ENV.reagent;
View compiled
__webpack_require__
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:659
  656 | };
  657 | 
  658 | // Execute the module function
> 659 | modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
  660 | 
  661 | // Flag the module as loaded
  662 | module.l = true;
View compiled
fn
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:85
  82 | 		console.warn("[HMR] unexpected require(" + request + ") from disposed module " + moduleId);
  83 | 		hotCurrentParents = [];
  84 | 	}
> 85 | 	return __webpack_require__(request);
  86 | };
  87 | var ObjectFactory = function ObjectFactory(name) {
  88 | 	return {
View compiled
(anonymous function)
node_modules/shadow-cljs/alpha.core.js:4
  1 | var CLJS_ENV = require("./cljs_env");
  2 | var COMPILED = false;
  3 | require("./ajax.core.js");
> 4 | require("./reagent.core.js");
  5 | require("./cljs.core.js");
  6 | require("./alpha.config.js");
  7 | require("./re_frisk.core.js");
View compiled
__webpack_require__
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:659
  656 | };
  657 | 
  658 | // Execute the module function
> 659 | modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
  660 | 
  661 | // Flag the module as loaded
  662 | module.l = true;
View compiled
fn
/Users/yuzhao/gits/alpha-app/webpack/bootstrap 80a3781f869adb4ebbaf:85
  82 | 		console.warn("[HMR] unexpected require(" + request + ") from disposed module " + moduleId);
  83 | 		hotCurrentParents = [];
  84 | 	}
> 85 | 	return __webpack_require__(request);
  86 | };
  87 | var ObjectFactory = function ObjectFactory(name) {
  88 | 	return {
View compiled
(anonymous function)
src/index.js:1
> 1 | import {init} from 'shadow-cljs/alpha.core';
  2 | 
  3 | init();
  4 | 
View compiled
▶ 6 stack frames were collapsed.
This screen is visible only in development. It will not appear if the app crashes in production.
Open your browser’s developer console to further inspect this error.

Handle conflicting local/global NPM versions

If there is a global shadow-cljs install it will be used over the version in the project which means updating shadow-cljs must be done twice. The project technically doesn't need the local install but it is safer and makes the package.json self contained.

Need to investigate how gulp and gulp-cli do that.

See: #38 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.