Giter Site home page Giter Site logo

workspaces's Introduction

workspaces

Workspaces is a component development environment for ClojureScript, inspired by devcards.

Workspaces allows you to create cards with your interface components so you don’t need to render your entire application. It also allows you to create cards for your tests so you don’t need to switch to the terminal to see the results. You can create tabs, a.k.a "workspace", and manage your cards size and position within it.

Workspaces Main

First add the workspaces dependency on your project.

Clojars Project

This is recommended template for the HTML file for the workspaces:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
  </head>
  <body>
    <div id="app"></div>
    <!-- you might need to change the js path depending on your configuration -->
    <script src="/js/workspaces/main.js" type="text/javascript"></script>
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css">
  </body>
</html>

Then create the entry point for the workspaces:

(ns my-app.workspaces.main
  (:require [nubank.workspaces.core :as ws]
            ; require your cards namespaces here
            ))

(defonce init (ws/mount))

NEW: Workspaces now has a custom shadow-cljs build target. If you use it, it will:

  • Auto-scan for all workspace cards and tests (based on a regex you supply)

  • Auto-configures the "main" module based on that scan.

  • Auto-detects when you add new files.

;; shadow-cljs configuration
{:builds {:cards {:target     nubank.workspaces.shadow-cljs.target
                  :ns-regexp  "-(test|cards)$"
                  :output-dir "resources/public/js/workspaces"
                  :asset-path "/js/workspaces"
                  :preloads   [] ; optional, list namespaces to be pre loaded
                  }}

You may still configure it with the old style with the :browser target, but in that case you will always have to add your workspace and test namespaces to your main.cljs file.

;; shadow-cljs configuration
{:builds {:workspaces {:target           :browser
                       :output-dir       "resources/public/js/workspaces"
                       :asset-path       "/js/workspaces"
                       :devtools         {;:preloads   [fulcro.inspect.preload ] ; include for Fulcro Inspect support
                                          :http-root          "resources/public"
                                          :http-port          3689
                                          :http-resource-root "."
                                          :preloads []}
                       :modules          {:main {:entries [my-app.workspaces.main]}}}}}

Now run with:

npx shadow-cljs watch workspaces

For some reason on my tests I couldn’t make Figwheel :on-jsload hook to work with library code, to go around this modify the main to look like this:

(ns my-app.workspaces.main
  (:require [nubank.workspaces.core :as ws]
            ; require your cards namespaces here
            ))

(defn on-js-load [] (ws/after-load))

(defonce init (ws/mount))

So you can call the hook from there, as:

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

                :figwheel     {:on-jsload "myapp.workspaces.main/on-js-reload"}

                :compiler     {:main                 myapp.workspaces.main
                               :asset-path           "js/workspaces/out"
                               :output-to            "resources/public/js/workspaces/main.js"
                               :output-dir           "resources/public/js/workspaces/out"
                               :source-map-timestamp true
                               :preloads             [devtools.preload]}}]}

Now run with:

lein figwheel

To define cards you use the ws/defcard macro, here is an example to create a React card:

(ns myapp.workspaces.cards
  (:require [nubank.workspaces.core :as ws]
            [nubank.workspaces.card-types.react :as ct.react]))

; simple function to create react elemnents
(defn element [name props & children]
  (apply js/React.createElement name (clj->js props) children))

(ws/defcard hello-card
  (ct.react/react-card
    (element "div" {} "Hello World")))

You can use this to mount any React component, for a re-frame for example, you can use (reagent/as-element [re-frame-root]) as the content. For a complete re-frame demo check these sources.

Usually libraries like Fulcro or Re-frame will manage the state and trigger render in the proper times, but if you wanna do something with raw React, you can provide an atom to be the app state, and the card will watch that atom and triggers a root render everytime it changes.

(ws/defcard counter-example-card
  (let [counter (atom 0)]
    (ct.react/react-card
      counter
      (element "div" {}
        (str "Count: " @counter)
        (element "button" {:onClick #(swap! counter inc)} "+")))))

Important: The react-card is actually a macro, the reason is that we wrap your render call into a function that will only be called when that card is initialized. This prevents the render calls to happen when cards are just loading.

Workspaces is built with Fulcro and has some extra support for it. Using the fulcro-card you can easely mount a Fulcro component with the entire app, here is an example:

(ns myapp.workspaces.fulcro-demo-cards
  (:require [fulcro.client.primitives :as fp]
            [fulcro.client.localized-dom :as dom]
            [nubank.workspaces.core :as ws]
            [nubank.workspaces.card-types.fulcro :as ct.fulcro]
            [nubank.workspaces.lib.fulcro-portal :as f.portal]
            [fulcro.client.mutations :as fm]))

(fp/defsc FulcroDemo
  [this {:keys [counter]}]
  {:initial-state (fn [_] {:counter 0})
   :ident         (fn [] [::id "singleton"])
   :query         [:counter]}
  (dom/div
    (str "Fulcro counter demo [" counter "]")
    (dom/button {:onClick #(fm/set-value! this :counter (inc counter))} "+")))

(ws/defcard fulcro-demo-card
  (ct.fulcro/fulcro-card
    {::f.portal/root FulcroDemo}))

By default the Fulcro card will wrap your component with a thin root, by having always having components with idents you can leverage generic mutations, this is recommended over making a special Root. But if you want to send your own root, you can set the ::f.porta/wrap-root? false. Here are more options available:

  • ::f.portal/wrap-root? (default: true) Wraps component into a light root

  • ::f.portal/app (default: {}) This is the app configuration, same options you could send to fulcro/new-fulcro-client

  • ::f.portal/initial-state (default {}) Accepts a value or a function. A value will be used to call the initial state function of your root. If you provide a function, the value returned by it will be the initial state.

  • ::f.portal/root-state This map will be merged into the app root state to be part of the initial state in the root, this is useful to set things like :ui/locale considering that a wrapped root initial state will not end up in the root (will be in :ui/root).

  • ::f.portal/computed Add this computed data to the root factory props

  • ::f.portal/root-node-props use this to send props into the root note created to mount the portal on.

When you use a Fulcro card you will notice it has an extra toolbar, in this toolbar you have two action buttons:

  • Inspect: this is an integration with Fulcro Inspect, if you have the extension active on Chrome, it will select the application of the card for inspection.

  • Restart: this will do a full refresh on app, unmount and mount again

Fulcro 3 is a rewrite and require new wrappers, for Fulcro 3 we got the portal and the card type in the namespace nubank.workspaces.card-types.fulcro3. The settings are the same as for Fulcro 2, all keywords should be namespaced with the fulcro3 card type namespace.

Workspaces has default integration with cljs.test, but you have to start the tests using ws/deftest instead of cljs.test/deftest. The ws/deftest will also emit a cljs.test/deftest call, so you can use the same for running on CI. Example test card:

(ws/deftest sample-test
  (is (= 1 1)))

When you create test cards using ws/deftest, a card will be automatically created to run on the test on that namespace, just click on the test namespace name on the index to load the card.

When you add any test, you also get a card that will run the whole test suite. You can open this card by clicking at the TESTS in the index, or using spotlight to find the test-all card.

You can define settings for your card, like what initial size it should have, to do that you can add maps to the card definition:

(ns myapp.workspaces.configurated-cards
  (:require [nubank.workspaces.core :as ws]
            [nubank.workspaces.model :as wsm]))

(ws/defcard sized-card
  {::wsm/card-width 5
   ::wsm/card-height 7}
  (ct.react/react-card
    (dom/div "Foo")))

The measurement is in grid tiles. A recommended way to define a card size is to add it in default size to workspace, resize it to the appropriated size, then use the Size button accessible from the more icon in the card header, the card current size will be logged to the browser console.

For the built-in cards you can also determine how the element will be positioned in the card. So far we have been using the center card position but depending on the kind of component you are trying that might not be the best option.

(ws/defcard positioned-top
  {::wsm/card-width  5
   ::wsm/card-height 7
   ::wsm/align       {:flex 1}}
  (ct.react/react-card
    (dom/div "Foo on top")))

The card container is a flex element, so the previous example will put the card on top and make it occupy the full width of the container.

The default ::wsm/align is:

{:display         "flex"
 :align-items     "center"
 :justify-content "center"}

Using the key ::wsm/node-props you can set the style or other properties of the container node.

(ws/defcard styles-card
  {::wsm/node-props {:style {:background "red" :color "white"}}}
  (ct.react/react-card
    (dom/div "I'm in red")))

You will probably find some combinations of card settings you keep repeating, it’s totally ok to put those in variables and re-use. You can also send as many configuration maps as you want, in fact the return of (ct.react/react-card) is also a map, they all just get merged and stored as the card definition.

(def purple-card {::wsm/node-props {:style {:background "#79649a"}}})
(def align-top {::wsm/align {:flex 1}})

(ws/defcard widget-card
  {::wsm/card-width 3 ::wsm/card-height 7}
  purple-card
  align-top
  (ct.react/react-card
    (dom/div "💜")))

Now that we know how to define cards, it’s time to learn how to work with then.

Imagine when you are about to start working on some components of your project, you can start by looking at the index or searching using the spotlight feature (alt+shift+a).

By clicking on the card names you will add then to the current workspace (one will be created if you don’t have any open).

The idea here is that you add just the cards there are relevant to the work you need to do, and create a workspace that can make the best use of your screen pixels.

And workspaces comes on tabs, enabling you to quickly switch between different workspace settings.

The following topics will describe what you can do to help you manage your workspaces.

You can create new workspaces by clicking at the + tab on the interface. The workspaces are created and stored in your browser local storage. You can rename the workspace by clicking on its tab while it’s active.

Your cards are placed in a responsive grid, this means that the number of columns you have available will vary according to your page width size. Right below the workspace tabs you can see how many columns you have available right now (eg: c8 means 8 columns).

Each responsive breakpoint will have stored separated, so you can arange a workspace to fit that available width. The sizes and positions will be recorded separated by each column numbers (they vary from 2 to 20).

Each column size has 120~140px, varies depending on page width.

In the card header you will see the card title (which is the name of the card symbol) on the left, and at the right two icons. The first icon is the ``more'', mouse over it to see some card available actions:

  • Source: open a modal with the card source code

  • Solo: open a new workspace containing just this card using the whole workspace space

  • Size: prints the current card size in the browser console

  • Remount: dispose the card and start it over

After that you have an X icon to remove the card from current workspace.

Sometimes you want to focus on a single card, like when you want to see just the full test suite or want to have a card that renders your entire app.

In these cases you can open a tab with a card occupying the whole space, you can do that clicking in the Solo button from the card actions, or via spotlight, holding the alt key when clicking or hitting return to select.

When you have an open workspace, there is a toolbar with some action buttons, here is a description of what each does:

  • Copy layout: actually a select here, use this to copy the layout from a different responsive breakpoint

  • Refresh cards: triggers a refresh on every card on this active workspace

  • Duplicate: creates a copy of current workspace

  • Unify layouts: makes every breakpoint have the same layout as the current active one

  • Export: Export current workspace layouts to data (logged into browser console)

  • Delete: Delete current workspace

A lot of times your workspaces will be disposable, just pull a few components, work and throw away. But other times you like to create more durable ones, like a kitchen sink of all your components buildings blocks, or maybe a setup that works nice for a specific task. You a lot of effort to make it look good on many different responsive breakpoints. So would be a pain if every user of the system had to redo the task to organize those types of workspaces.

To solve that, you can use the Export button on the workspace toolbar. It outputs the workspace layout as a transit data on the console. You can copy that, and use to store that workspace setup on the code, making it available to any other person using this workspace setup.

(ws/defworkspace ui-block
  "[\"^ \",\"c10\",[[\"^ \",\"i\",\"~$fulcro.incubator.workspaces.ui.reakit-ws/reakit-base\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"minH\",2]],\"c8\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c16\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c14\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c2\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c12\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c4\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c18\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c20\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]],\"c6\",[[\"^ \",\"i\",\"^0\",\"w\",2,\"h\",4,\"x\",0,\"y\",0,\"^1\",2]]]"))

When you open a shared workspace, you can’t change it, it’s static, but you can duplicate it and change the copy as you please.

Here is a list of available shortcuts, all of then use alt+shift followed by a key:

  • alt+shift+a: Add card to current workspace (open spotlight for card picking)

  • alt+shift+i: Toggle index view

  • alt+shift+h: Toggle card headers

  • alt+shift+n: Create new local workspace

  • alt+shift+w: Close current workspace

To demonstrate what a custom card takes to be created, let’s take the following example:

(ws/defcard custom-card
  {::wsm/init
   (fn [card]
     {::wsm/render
      (fn [node]
        (gdom/setTextContent node (str "Rendering card " (::wsm/card-id card))))})})

So card definitions are also maps. The ::wsm/init will be called upon card initialization.

In next section we will learn about the card life cycle and how you can hook on it.

The card life cycle happens according the following events:

When cards are loaded, their settings are stored locally in an atom. Workspaces tries to make this process as light as possible, adding many cards should have the minimum overhead possible, cards are not initialized until they are placed in a visible workspace.

When the card is initialized, the map returned by it will be stored and used to manage the card while it lives.

A card is a shared unit across workspaces, so if you have a card on a active workspace and add the same card to another workspace, it will just call a new render, but not a new initialization (they potentially will share state, but that might vary depending on the card implementation).

The render system is based on HTML nodes, you provide a render function and workspaces will call that function with a HTML node so you can render/mount your component in it.

The definition from render (and other life cycle functions) will come from calling ::wsm/init on your card.

Here is an example of a custom card with a basic render:

(ws/defcard custom-card
  {::wsm/init
   (fn [card]
     {::wsm/render
      (fn [node]
        (gdom/setTextContent node "Hello custom card!"))})})

A refresh is intended to force a new render of the component. In the beginning of these docs we asked you setup the load hook nubank.workspaces.core/after-load, this hook will refresh every card in the active workspace. In pratice it will call the ::wsm/refresh method in your card, let’s see an example by extending our previous custom card to handle refresh.

(ws/defcard custom-card
  {::wsm/init
   (fn [card]
     (let [counter (atom 0)]
       {::wsm/refresh
        (fn [node]
          (gdom/setTextContent node (str "Card updated, count: " (swap! counter inc) "!")))

        ::wsm/render
        (fn [node]
          (gdom/setTextContent node (str "Card rendered, count: " counter "!")))}))})

You can try clicking in the ``Refresh cards'' button in the workspace toolbar and see the counter updating on every refresh.

There is one exception to this flow, and that is when you change anything about the card definition itself. Workflows will detect when the card has changed (by comparing the old form with the new form) and when it changes, the whole card is disposed and remounted.

A card is disposed when all it’s active references are removed from the open workspaces. When you remove a card from a workspace, it might get disposed, but only if this card is not present in any of the other open workspaces (living in tabs). This will give you a chance to free resources from that card.

(ws/defcard custom-card
  {::wsm/init
   (fn [card]
     (let [counter (atom 0)]
       {::wsm/dispose
        (fn [node]
          ; doesn't make a real difference for resource cleaning, just a dummy example
          ; so you can replace the code
          (gdom/setTextContent node ""))

        ::wsm/refresh
        (fn [node]
          (gdom/setTextContent node (str "Card updated, count: " (swap! counter inc) "!")))

        ::wsm/render
        (fn [node]
          (gdom/setTextContent node (str "Card rendered, count: " @counter "!")))}))})

If we try to use our alignment settings with our new card, you will see it will not work.

This is because the alignment implementation is a wrapper utility, and you have to manually call it to get it’s functionality, let’s see how we can extend our card to support it:

(ws/defcard custom-card
  {::wsm/align {:flex 1}
   ::wsm/init
   (fn [card]
     (let [counter (atom 0)]
       ; wrap our definition with positioned.card, from nubank.workspaces.card-types.util
       (ct.util/positioned-card card
         {::wsm/dispose
          (fn [node]
            ; doesn't make a real difference for resource cleaning, just a dummy example
            ; so you can replace the code
            (gdom/setTextContent node ""))

          ::wsm/refresh
          (fn [node]
            (gdom/setTextContent node (str "Card updated, count: " (swap! counter inc) "!")))

          ::wsm/render
          (fn [node]
            (gdom/setTextContent node (str "Card rendered, count: " @counter "!")))})))})

Now we can use the ::wsm/align as usual. I like to point out you can use this strategy yourself to create wrapper functions that can add functionality to a card definition, they are good composition blocks.

Here let’s create a custom implementation for a React card, this implementation will assume the app state is an atom, and will have a timer ticking in a root property on the state atom.

; it's a good pattern to have the card init function separated from the card function
; this will make easier for others to use your card as a base for extension.
(defn react-timed-card-init [card state-atom component]
  (let [{::wsm/keys [dispose refresh render] :as react-card} (ct.react/react-card-init card state-atom component)
        timer (js/setInterval #(swap! state-atom update ::ticks inc) 1000)]
    (assoc react-card
      ::wsm/dispose
      (fn [node]
        ; clean the timer on dispose
        (js/clearInterval timer)
        (dispose node))

      ::wsm/refresh
      (fn [node]
        (refresh node))

      ::wsm/render
      (fn [node]
        (render node)))))

(defn react-timed-card [state-atom component]
  {::wsm/init #(react-timed-card-init % state-atom component)})

(ws/defcard react-timed-card-sample
  (let [state (atom {})]
    (react-timed-card state
      ; note since we are not using the macro it's better to send a function to avoid
      ; premature rendering
      (fn []
        (dom/div (str "Tick: " (::ticks @state)))))))

To add a toolbar, you must provide the ::wsm/render-toolbar. This time you must return a React component that will be used as the toolbar. We suggest using components from the namespace nubank.workspaces.ui.core for consistency.

(defn react-timed-card-init [card state-atom component]
  (let [{::wsm/keys [dispose refresh render] :as react-card} (ct.react/react-card-init card state-atom component)
        timer (js/setInterval #(swap! state-atom update ::ticks inc) 1000)]
    (assoc react-card
      ::wsm/dispose
      (fn [node]
        ; clean the timer on dispose
        (js/clearInterval timer)
        (dispose node))

      ::wsm/refresh
      (fn [node]
        (refresh node))

      ::wsm/render
      (fn [node]
        (render node))

      ::wsm/render-toolbar
      (fn []
        (dom/div
          (uc/button {:onClick #(js/console.log "State" @state-atom)} "Log app state"))))))

Use this provide extra functionatility for your cards.

You might noticed that the test cards are able to change the card header style to reflect the test status, and you can do this to your cards too.

Let’s add a button on our toolbar to change the header color:

      ::wsm/render-toolbar
      (fn []
        (dom/div
          (uc/button {:onClick #((::wsm/set-card-header-style card) {:background "#cc0"})} "Change header color")
          (uc/button {:onClick #(js/console.log "State" @state-atom)} "Log app state")))

By calling the ::wsm/set-card-header-style you can set any css you want to the header.

That’s all, go make some nice cards!

If you have any questions, hit us at the #workspaces channel on Clojurians.

workspaces's People

Contributors

abhi18av avatar awkay avatar dependabot[bot] avatar eoliphan avatar ericdallo avatar felipethome avatar isabelay avatar johannesloetzsch avatar jorgeviana avatar ssyh avatar wilkerlucio 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  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

workspaces's Issues

Test cards with Fulcro 3

It seems to me, that currently Test cards don't work when using Fulcro 3.

I tried the f3 branch with workspaces/nubank/workspaces/workspaces/cards.cljs, examples/workspaces-shadow-example and fulcro-template but in all cases it failed to render the Test cards.

I would very appreciate, if you could provide a working example.

Improve README for newcomers

Hello! As a person completely new to this, I would appreciate if the section https://github.com/nubank/workspaces#workspaces explained briefly what this is all about and what is a card, before jumping to the setup. (I can go read it on the DevCards page but it would be nice to have it here as well.)

Also, the animated gif is too fast to follow. + perhaps link to a demo video, if there is one?

Thank you!

Additional setup required for shadow-cljs

I had to add a few steps to get workspaces working with shadow-cljs. Not sure if it is something specific to my setup, but I thought I would document it here.

deps.edn

nubank/workspaces {:mvn/version "1.1.2"
                   :exclusions [cljsjs/highlight.cljs]}

shadow-cljs.edn

:js-options {:resolve {"highlight" {:target :npm :require "highlight.js"}}}
:build-options {:ns-aliases {highlight highlight.js}}

package.json

"highlight.js": "^9.12.0",

workspaces/main.cljs

(:require ["highlight.js" :as highlight])

(js/goog.exportSymbol "hljs" highlight)

shadow-cljs target broken

In shadow-cljs 2.9.x I removed the classpath indexing since it was causing a lot of issues and didn't provide much. It did however provide the classpath/get-all-resources fn which is used here:

(->> (cp/get-all-resources classpath)

This no longer provides the necessary data and fails. I migrated all the official test targets to use the alternative instead which restores that functionality. You could either use the provided helper fn directly or use the replacement find-cljs-namespaces-in-files fn if you want more custom filtering.

shadow-cljs: java.lang.NoSuchMethodError: com.cognitect.transit.TransitFactory.writer

Currently, with shadow-cljs 2.4.33 and [nubank/workspaces "1.0.0-preview6"], when I run cider-jack-in I receive the following exception:

shadow-cljs - config: /Users/will/Code/lilactown/shadow-cljs.edn  cli version: 2.4.33  node: v10.3.0
shadow-cljs - updating dependencies
shadow-cljs - dependencies updated
shadow-cljs - starting ...
Aug 08, 2018 3:49:28 PM org.xnio.Xnio <clinit>
INFO: XNIO version 3.3.8.Final
Aug 08, 2018 3:49:28 PM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.3.8.Final
Exception in thread "main" java.lang.NoSuchMethodError: com.cognitect.transit.TransitFactory.writer(Lcom/cognitect/transit/TransitFactory$Format;Ljava/io/OutputStream;Ljava/util/Map;Lcom/cognitect/transit/WriteHandler;Ljava/util/function/Function;)Lcom/cognitect/transit/Writer;
	at cognitect.transit$writer.invokeStatic(transit.clj:157)
	at cognitect.transit$writer.invoke(transit.clj:139)
	at shadow.build.cache$write_stream.invokeStatic(cache.clj:9)
	at shadow.build.cache$write_stream.invoke(cache.clj:8)
	at shadow.build.cache$write_file.invokeStatic(cache.clj:26)
	at shadow.build.cache$write_file.invoke(cache.clj:24)
	at shadow.build.classpath$find_jar_resources.invokeStatic(classpath.clj:581)
	at shadow.build.classpath$find_jar_resources.invoke(classpath.clj:552)
	at shadow.build.classpath$find_resources.invokeStatic(classpath.clj:622)
	at shadow.build.classpath$find_resources.invoke(classpath.clj:620)
	at shadow.build.classpath$index_path_STAR_.invokeStatic(classpath.clj:842)
	at shadow.build.classpath$index_path_STAR_.invoke(classpath.clj:839)
	at clojure.lang.PersistentVector.reduce(PersistentVector.java:341)
	at clojure.core$reduce.invokeStatic(core.clj:6747)
	at clojure.core$reduce.invoke(core.clj:6730)
	at shadow.build.classpath$index_classpath$fn__11559$fn__11560.invoke(classpath.clj:957)
	at clojure.lang.Atom.swap(Atom.java:37)
	at clojure.core$swap_BANG_.invokeStatic(core.clj:2344)
	at clojure.core$swap_BANG_.invoke(core.clj:2337)
	at shadow.build.classpath$index_classpath$fn__11559.invoke(classpath.clj:957)
	at shadow.build.classpath$index_classpath.invokeStatic(classpath.clj:956)
	at shadow.build.classpath$index_classpath.invoke(classpath.clj:951)
	at shadow.build.classpath$index_classpath.invokeStatic(classpath.clj:953)
	at shadow.build.classpath$index_classpath.invoke(classpath.clj:951)
	at shadow.cljs.devtools.server.common$fn__16669.invokeStatic(common.clj:74)
	at shadow.cljs.devtools.server.common$fn__16669.invoke(common.clj:72)
	at clojure.lang.AFn.applyToHelper(AFn.java:154)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.core$apply.invoke(core.clj:652)
	at shadow.runtime.services$start_one.invokeStatic(services.clj:98)
	at shadow.runtime.services$start_one.invoke(services.clj:87)
	at shadow.runtime.services$start_many$fn__8978.invoke(services.clj:127)
	at shadow.runtime.services$start_many.invokeStatic(services.clj:126)
	at shadow.runtime.services$start_many.invoke(services.clj:105)
	at shadow.runtime.services$start_all.invokeStatic(services.clj:144)
	at shadow.runtime.services$start_all.invoke(services.clj:139)
	at shadow.cljs.devtools.server$start_system.invokeStatic(server.clj:279)
	at shadow.cljs.devtools.server$start_system.invoke(server.clj:163)
	at shadow.cljs.devtools.server$start_BANG_.invokeStatic(server.clj:391)
	at shadow.cljs.devtools.server$start_BANG_.invoke(server.clj:324)
	at shadow.cljs.devtools.server$start_BANG_.invokeStatic(server.clj:327)
	at shadow.cljs.devtools.server$start_BANG_.invoke(server.clj:324)
	at shadow.cljs.devtools.server$from_cli.invokeStatic(server.clj:523)
	at shadow.cljs.devtools.server$from_cli.invoke(server.clj:499)
	at shadow.cljs.devtools.cli$blocking_action.invokeStatic(cli.clj:143)
	at shadow.cljs.devtools.cli$blocking_action.invoke(cli.clj:130)
	at shadow.cljs.devtools.cli$main.invokeStatic(cli.clj:191)
	at shadow.cljs.devtools.cli$main.doInvoke(cli.clj:146)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:661)
	at clojure.core$apply.invoke(core.clj:652)
	at shadow.cljs.devtools.cli$_main.invokeStatic(cli.clj:233)
	at shadow.cljs.devtools.cli$_main.doInvoke(cli.clj:231)
	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.main$main_opt.invokeStatic(main.clj:317)
	at clojure.main$main_opt.invoke(main.clj:313)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)

Downgrading to [nubank/workspaces "1.0.0-preview5"] allows CIDER to start nREPL and connect successfully.

Can't compile nubank.workspaces.shadow-cljs.target

After updating shadow-cljs, I get...

X Compilation failed.
failed to require :target nubank.workspaces.shadow-cljs.target for build :workspaces
Error in phase :compile-syntax-check
RuntimeException: No such var: tu/find-namespaces-by-regexp

It looks like this shadow-cljs commit renamed some things
thheller/shadow-cljs@c94c77d

which breaks https://github.com/nubank/workspaces/blob/3ad83b10fb08efd5614cdd503159aa151c9ff7fc/src/nubank/workspaces/shadow_cljs/target.clj

The required namespace "fulcro.inspect.client" is not available

I'm currently using this in a vanilla React project. I get this error when compilation begins with the latest version 1.0.0-preview6:

shadow.user> (shadow/watch :workspaces)
[:workspaces] Configuring build.
[:workspaces] Compiling ...
[:workspaces] Build failure:
The required namespace "fulcro.inspect.client" is not available, it was required by "nubank/workspaces/card_types/fulcro.cljs".

Downgrading to 1.0.0-preview5 works as expected.

Small shadow-cljs related tweaks

Hey there, this is really cool!

You could make some small tweaks to make config a bit easier.

The myapp.workspaces.main could be completely optional if you instead change the build config to:

:modules
{:main
    {:entries
     [myapp.workspaces.cards
      myapp.workspaces.custom-card
      myapp.workspaces.fulcro-demo-cards
      myapp.workspaces.reframe-demo-cards]
     :init-fn nubank.workspaces.core/mount}}

It is exactly equivalent to the current .main in the examples directory. Totally fine to use the ns over the config though. With some small adjustments you could also use the :browser-test target instead and use :ns-regexp "-cards$" so you don't have to list them all.

You can also change the nubank.workspaces.core to use the lifecycle metadata hooks so the user doesn't have to configure them at all.

(defn before-load
  {:dev/before-load true}
  []
  (reset! data/card-definitions-snap* @data/card-definitions*))

(defn after-load
  {:dev/after-load true}
  []
  (ui/refresh-active-workspace-cards (:reconciler @data/app*))
  (reset! data/card-definitions-snap* @data/card-definitions*))

Error: Externs must contain builtin for env BROWSER: whatwg_console.js

My shadow-cljs.edn configuration is as follows:

{:source-paths ["src"]

 :dependencies [[proto-repl "0.3.1"]
                [reagent "0.9.0-rc2"]
                [re-frame "0.11.0-rc1"]
                [day8.re-frame/http-fx "0.1.6"]
                [day8.re-frame/re-frame-10x "0.4.4"]
                [day8.re-frame/tracing "0.5.1"]
                [cljs-ajax "0.7.3"]
                [com.andrewmcveigh/cljs-time "0.5.2"]
                [bidi "2.1.6"]
                [kibu/pushy "0.3.8"]
                [binaryage/devtools "0.9.10"]
                [nubank/workspaces "1.0.12"]
                [cider/cider-nrepl "0.21.0"]
                [stylefy "1.14.0"]
                [com.rpl/specter "1.1.2"]]

 :nrepl        {:port 3333}

 :builds       {:app {:target :browser
                      :output-dir "public/js"
                      :asset-path "/js"

                      :modules {:main {:init-fn vds.demo.core/init}}

                      :dev {:compiler-options {:closure-defines {re-frame.trace/trace-enabled? true
                                                                 day8.re-frame.tracing/trace-enabled? true
                                                                 re_frame.trace.trace_enabled_QMARK_  true
                                                                 }
                                               :infer-externs true}}

                      :devtools {:http-root   "public"
                                 :http-port   2772
                                 :after-load  vds.usage.core/mount-root
                                 :preloads    [day8.re-frame-10x.preload]}}
                :cards {:target     nubank.workspaces.shadow-cljs.target
                        :ns-regexp  "cards*"
                        :output-dir "public/js/workspaces"
                        :asset-path "/js/workspaces"
                        :preloads   [] ; optional, list namespaces to be pre loaded
                        }}}

Whenever I run shadow-cljs server in my application I am getting the following errors:

shadow-cljs server

shadow-cljs - config: /Users/circle/Project/vadelabs/vade-design-system/shadow-cljs.edn  cli version: 2.8.59  node: v10.16.3
Execution error (IllegalStateException) at com.google.common.base.Preconditions/checkState (Preconditions.java:823).
Externs must contain builtin for env BROWSER: whatwg_console.js

But as soon as I remove nubank/workspaces dependency it starts working.

Full screen mode

Having a full screen mode (like solo) might be useful for when you want to develop a mobile friendly app and want to use chromes devtools to look at different size screens and orientations.

Definition for standard alignments

Currently, there are undocumented features that support named (keywords) alignment instead of using the styles map directly. We can investigate better the most common patterns so users don't have to manually write the CSS for it.

Issues with the in-repo example

Hi @wilkerlucio,

I was experimenting with the workspaces-shadow-example by cloning the repo and followed the setup instructions with

npm install
npx shadow-cljs watch workspaces 

However, this raises the following error

Abhinavs-MacBook-Pro:workspaces-shadow-example eklavya$ npx shadow-cljs watch workspaces
shadow-cljs - config: /Users/eklavya/projects/work/FulcroLogic/workspaces/examples/workspaces-shadow-example/shadow-cljs.edn  cli version: 2.8.42  node: v12.6.0
shadow-cljs - starting via "clojure"
Execution error (FileNotFoundException) at clojure.main/main (main.java:40).
Could not locate shadow/cljs/devtools/cli__init.class, shadow/cljs/devtools/cli.clj or shadow/cljs/devtools/cli.cljc on classpath.

Full report at:
/var/folders/zp/63677vtx23d_b2_nd7mm92040000gn/T/clojure-9194637241821148498.edn

Do you have any suggestions as to how can I get this working?

build with example setup fails: goog.debug.Logger.level missing, required by fulcro/logging.cljc

From what I learned, google closure and cljs removed that api, but fulcro2 still depends on it.

I've just set up a project and ran into this:

mathiasp:~/Projekte/helix-play% cat shadow-cljs.edn 
;; shadow-cljs configuration
{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]

 :dependencies
 [[nubank/workspaces "1.1.2"]]

 :builds
 {:cards {:target     nubank.workspaces.shadow-cljs.target
          :ns-regexp  "-(test|cards)$"
          :output-dir "resources/public/js/workspaces"
          :asset-path "/js/workspaces"
          :preloads   [] ; optional, list namespaces to be pre loaded
          }}}
mathiasp:~/Projekte/helix-play% npx shadow-cljs watch cards          
shadow-cljs - config: /home/mathiasp/Projekte/helix-play/shadow-cljs.edn
shadow-cljs - server version: 2.17.2 running at http://localhost:9630
shadow-cljs - nREPL server started on port 30982
shadow-cljs - watching build :cards
[:cards] Configuring build.
[:cards] Compiling ...
[:cards] Build failure:
The required namespace "goog.debug.Logger.Level" is not available, it was required by "fulcro/logging.cljc".

This is with node 14 & 17 and OpenJDK 17.

I'm not really experienced with cljs and node dependency handling, any tips on how I can get around this?

Any help apreciated,

Mathias

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.