Fork me on GitHub
#shadow-cljs
<
2021-11-29
>
romdoq12:11:56

This may be a strange/silly question, but is there a way for the shadow-cljs dev server to share dependencies with a running webpack dev server? I have a re-frame project which is used as an :npm-module and included by our main web app. We also have some packages that are shared between the main app and the cljs project. Some of these shared packages require that only one instance be imported at once (singletons), for which we use peerDependencies. However, as best as I can tell, the shadow-cljs dev server imports a second instance of those packages in its output, so the browser hits their singleton guards and fails. 😕 I hope that was clear enough. 😅 Any suggestions?

thheller16:11:16

I do not understand the problem. peerDependencies does nothing regarding how a package is required/used at runtime

thheller16:11:45

if you use regular :npm-module build and let webpack post process the dependencies then there should be no issue since shadow-cljs will not be providing any JS deps

thheller16:11:02

if you however configured :js-provider or something then you'll get duplicates

romdoq13:11:15

Should not providing the JS deps also be true of the shadow dev server, eg. shadow-cljs watch npm, when it is being yarn linked by the webpacked project? That's the situation we're having difficulty with Maybe there's some setting we've goofed. Though :js-provider certainly isn't being set explicitly. :thinking_face:

romdoq14:11:52

Oh! I think it's because webpack sees the node_modules in the linked package, and bundles those as the (duplicated) deps to use for the linked module. It seems like the problematic packages only need to be installed in the :npm-module's node_modules for the Karma test suite to run :thinking_face:

Alexis Schad12:11:23

Hi there, I have trouble with :shadow as a :js-provider targeting node:

The required JS dependency "crypto" is not available, it was required by "node_modules/seedrandom/seedrandom.js".

...

crypto is part of the node-libs-browser polyfill package to provide node-native package support
for none-node builds. You should install shadow-cljs in your project to provide that dependency.

        npm install --save-dev shadow-cljs

See: 
I do have shadow-cljs installed locally. Did I miss something? (crypto is used by mathjs, required in my project)

mauricio.szabo12:11:36

@U01V2D5ALKX, sometimes shadow-cljs can't find all libraries because there are a lot of libs that use some kind of require.resolving or other tricks to find out if their dependencies are available. Do you have crypto npm installed? If so, is it a pure JS library, or is it a native (C/C++) library?

Alexis Schad12:11:21

crypto is a native node js library (https://nodejs.org/api/crypto.html), it is polyfilled in node-libs-browser for browser target. Here I need to include mathjs in my node build, and mathjs requires the native crypto (that must not be included in the build).

Alexis Schad13:11:48

I try to use a workaround

:resolve {"crypto" {:target :global                                           :global "crypto"}}
but it does not work.

Alexis Schad13:11:16

:resolve {"crypto" false} seems to work as a workaround

mauricio.szabo13:11:47

You can use :keep-as-requires, let me find it to you...

mauricio.szabo13:11:53

Here's my config for Chlorine (it's a node-library that uses :shadow as :js-provider):

:js-options {:js-provider :shadow
                     :keep-native-requires true
                     :keep-as-require #{"atom"}}

mauricio.szabo13:11:44

You can see if only adding :keep-native-requires true works. If it doesn't, change the :keep-as-require to #{"crypto"} and that should work 🙂

Alexis Schad13:11:56

It works! Thanks. I'll create a PR to add this into the user guide

thheller16:11:21

note that I do not recommend using :js-provider :shadow for node builds

thheller16:11:41

and therefore also not :keep-as-require or :keep-native-requires 😛

thheller16:11:16

better to do a regular build and post process with https://github.com/vercel/ncc if you must have a standalone file

Alexis Schad17:11:56

I want the closest behaviour to the :browser target (that I use too). Unless there's an unworking library with this setup, is there a reason why not tu use :shadow for node builds?

thheller17:11:17

:shadow is heavily optimized for the browser and usually not required for node

orestis15:11:49

Does shadow do something special to support (:require [goog]) , that the standalone CLJS compiler need configuration to support?

thheller16:11:44

goog is already an implicit dependency of every cljs namespace so you do not need it

orestis18:11:41

Yeah I didn't know that. Turns out there's some regression in CLJS: https://clojure.atlassian.net/browse/CLJS-3336

Ovidiu Stoica16:11:00

Hi there, I am trying to setup a aws-amplify-shadow-cljs project but I seem to have issues importing the amplify coding. Here are the issues: I seem to have similar issues to https://stackoverflow.com/questions/64461818/require-aws-amplify-v-3-amplify-and-auth-classes-in-clojurescript-reagent-shadow but even after I have done all those steps, it does not work. I will leave full error log in the comment to this post. Seems that from v4 of aws-amplify things don't work. shadow-cljs.edn:

{:source-paths ["src"]

 :dependencies [[proto-repl "0.3.1"]
                [binaryage/devtools "1.0.4"]
                [reagent "1.1.0"]
                [re-frame "1.2.0"]
                [day8.re-frame/re-frame-10x "1.1.13"]
                [bidi "2.1.5"]
                [kibu/pushy "0.3.8"]
                [com.andrewmcveigh/cljs-time "0.5.2"]]

 :nrepl        {:port 3333}

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

                      :modules          {:main {:init-fn app.core/init}}
                      :js-options       {:provider   :closure
                                         :entry-keys ["main" "module" "browser"]}

                      :compiler-options {:output-feature-set :es6
                                         :closure-defines    {re-frame.trace/trace-enabled?        true
                                                              day8.re-frame.tracing/trace-enabled? true}}

                      :devtools         {:http-root "public"
                                         :http-port 3000
                                         :preloads  [day8.re-frame-10x.preload]}}}}

Ovidiu Stoica16:11:12

Console errors about importing:

shadow.js.js:sourcemap:36 shadow-cljs - failed to load module$node_modules$aws_amplify$lib$index
shadow.js.jsRequire @ shadow.js.js:36
shadow.js.require @ shadow.js.js:59
eval @ app.core.js:2
goog.globalEval @ main.js:528
env.evalLoad @ main.js:1603
(anonymous) @ main.js:4961
main.js:1495 TypeError: util.inherits is not a function
    at Object.shadow$provide.module$node_modules$assert$assert (module$node_modules$assert$assert.js:12)
    at shadow.js.jsRequire (shadow.js.js:34)
    at Object.shadow$provide.module$node_modules$http2$lib$protocol$endpoint (module$node_modules$http2$lib$protocol$endpoint.js:2)
    at shadow.js.jsRequire (shadow.js.js:34)
    at Object.shadow$provide.module$node_modules$http2$lib$protocol$index (module$node_modules$http2$lib$protocol$index.js:1)
    at shadow.js.jsRequire (shadow.js.js:34)
    at Object.shadow$provide.module$node_modules$http2$lib$http (module$node_modules$http2$lib$http.js:9)
    at shadow.js.jsRequire (shadow.js.js:34)
    at Object.shadow$provide.module$node_modules$http2$lib$index (module$node_modules$http2$lib$index.js:1)
    at shadow.js.jsRequire (shadow.js.js:34)
reportError @ main.js:1495
env.evalLoad @ main.js:1605
(anonymous) @ main.js:4961
main.js:1497 The above error occurred when loading "app.core.js". Any additional errors after that one may be the result of that failure. In general your code cannot be trusted to execute properly after such a failure. Make sure to fix the first one before looking at others.
reportError @ main.js:1497
env.evalLoad @ main.js:1605
(anonymous) @ main.js:4961
shadow.module.main.append.js:4 An error occurred when calling (app.core/init)
eval @ shadow.module.main.append.js:4
goog.globalEval @ main.js:528
env.evalLoad @ main.js:1603
(anonymous) @ main.js:4962
main.js:1495 TypeError: app.core.init is not a function
    at eval (shadow.module.main.append.js:4)
    at eval (<anonymous>)
    at Object.goog.globalEval (main.js:528)
    at Object.env.evalLoad (main.js:1603)
    at main.js:4962
reportError @ main.js:1495
env.evalLoad @ main.js:1605
(anonymous) @ main.js:4962
main.js:1497 The above error occurred when loading "shadow.module.main.append.js". Any additional errors after that one may be the result of that failure. In general your code cannot be trusted to execute properly after such a failure. Make sure to fix the first one before looking at other

Ovidiu Stoica16:11:56

Core file

(ns app.core
  (:require
    ["/aws-exports.js" :default aws-exports]
    ["aws-amplify" :default amplify]
    [reagent.core :as r]
    [reagent.dom :as dom]
    [re-frame.core :as rf]
    [app.db]
    [app.router :as router]
    [app.components.core :as c]))


(def functional-compiler (r/create-compiler {:function-components true}))
(r/set-default-compiler! functional-compiler)

(defn pages
  [page-name]
  (case page-name
    :inboxes [inboxes]
    :profile [profile]
    :become-a-chef [become-a-chef]
    :recipes [recipes-page]
    :recipe [recipe-page]
    :log-in [log-in]
    :sign-up [sign-up]
    [recipes-page]))


(defn app
  []
  (let [active-page @(rf/subscribe [:active-page])]
    [:<>
     [:div.bg-gray-100.w-screen.h-screen
      [:div.container.mx-auto
       [:div
        [nav]
        [pages active-page]]]]]))

(defn ^:dev/after-load start
  []
  (dom/render
    [app]
    (.getElementById js/document "app")))

(defn ^:export init
  []
  (.configure amplify aws-exports)
  (router/start!)
  (rf/dispatch-sync [:initialize-db])
  (start))

Ovidiu Stoica16:11:23

package.json:

{
  "name": "Cheffy",
  "version": "0.0.1",
  "description": "Cheffy - AirBnB for Chefs",
  "private": true,
  "repository": {
    "type": "git",
    "url": ""
  },
  "scripts": {
    "shadow:watch": "shadow-cljs watch app",
    "shadow:release": "shadow-cljs release app",
    "postcss:build": "cross-env TAILWIND_MODE=build postcss src/css/tailwind.css -o ./public/css/main.css --verbose",
    "postcss:watch": "cross-env TAILWIND_MODE=watch postcss src/css/tailwind.css -o ./public/css/main.css --verbose -w",
    "postcss:release": "cross-env NODE_ENV=production postcss src/css/tailwind.css -o ./public/css/main.css --verbose",
    "dev": "run-p -l *:watch",
    "release": "run-s *:release"
  },
  "devDependencies": {
    "autoprefixer": "^10.3.3",
    "cssnano": "^5.0.8",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.3.5",
    "postcss-cli": "^8.3.1",
    "react-flip-move": "^3.0.2",
    "shadow-cljs": "^2.15.5",
    "tailwindcss": "^2.2.4"
  },
  "dependencies": {
    "@aws-amplify/ui-react": "^2.1.1",
    "@headlessui/react": "^1.4.2",
    "@heroicons/react": "^1.0.5",
    "@tailwindcss/forms": "^0.3.3",
    "aws-amplify": "^4.3.8",
    "aws-crt": "^1.10.2",
    "create-react-class": "15.7.0",
    "cross-env": "^7.0.3",
    "encoding": "^0.1.13",
    "highlight.js": "11.1.0",
    "http2": "^3.3.7",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  }
}

Ovidiu Stoica16:11:03

Other thing that is worth mentioning: The aws-amplify was requiring http2 module which (from what I remember) was built in with node js. I installed it with npm I http2 and there seem to be some errors related to this also

David Vujic16:11:52

There might be some issues when using the Amplify UI components, but I’m not sure if that is the issue for you. I’m using AWS Amplify v4 and have solved by building my app in two steps: clojurescript + webpack. This blog post (with links to a fully working example repo) might help you: https://davidvujic.blogspot.com/2021/08/hey-webpack-hey-clojurescript.html

❤️ 1
Ovidiu Stoica17:11:26

Hei @U018VMC8T0W! Yes, this is the problem, most likely! I will remake my repo after your demo! Thank you for the fast reply!

Ovidiu Stoica12:12:29

Did you also have this error? Seems that it gets this error trying to access process.env.BROWSERLIST_DISABLE_CACHE and process is undefined. I saw you also tried to install process as an npm package (did this also but it does not fix the issue) UDPATE: Added this to the webpack config and it was fixed

plugins: [
        // fix "process is not defined" error:
        // (do "npm install process" before running the build)
        new webpack.ProvidePlugin({
            process: 'process/browser',
        }),
    ],

👍 1
David Vujic12:12:53

In my case I added the process npm package, because there were conflicts with AWS Amplify and Storybook: both depending on Webpack, but with different versions. And Storybook used a feature that has been removed (process). So my workaround was to include it in the Storybook UI only. But I don’t think that package should be exposed in a web site (if I’m not mistaking).

David Vujic12:12:58

Just noticed your update: great!