reagent

A minimalistic ClojureScript interface to React.js http://reagent-project.github.io/
Kai 2021-02-21T04:22:03.003900Z

Hi, I’m having some trouble upgrading from aws-amplify-react to @aws-amplify/ui-react in a project. According to the https://docs.amplify.aws/ui/auth/authenticator/q/framework/react#migration, it should just be a matter of changing which package withAuthenticator is being imported from. However when I do this, I get Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function. I found https://stackoverflow.com/questions/61629092/failed-to-construct-htmlelement-please-use-the-new-operator but the solution offered there seems to just cause more errors within the aws package. I’ve spent today digging through the Reagent’s documentation on interop with React, but I can’t seem to find a solution. Does anyone have experience with this, or have any suggestions for how to get this to work?

juhoteperi 2021-02-21T11:11:37.004100Z

Might the new package uses ES6 modules instead of CJS, and you need to take this into account on :require forms, Shadow-cljs doc lists different require options for JS modules: https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages You cold use (js/console.log withAuthenticator) to check you are referring to the correct object.

Kai 2021-02-21T11:57:14.004300Z

I’ve already checked that unfortunately. (js/console.log withAuthenticator) prints the function as expected, so it seems like it’s being required correctly

rberger 2021-02-22T06:22:51.004600Z

After reading this, I looked at our code and saw that we’re still using the old aws-amplify-react and withAuthenticator I switched to use @aws-amplify/ui-react` and I’m seeing the same issue as @barnes.kai Looks like there is some discussion here https://github.com/thheller/shadow-cljs/issues/816 on the same issue, but without an explicit answer. I know nothing about webpack so don’t have any idea so far on how to implement / test the proposed solution.

Kai 2021-02-22T07:38:23.004900Z

Thanks @rberger, that clears up the cause of the issue then. I gave the webpack solution a try and after tweaking the suggested config a bit I have it generating the JS correctly, but I am now getting a lot of ReferenceError: shadow$bridge is not defined errors. I’m also not entirely satisfied with having to add in the separate webpack build step just for the sake of one dependency, so I think I might just use the old aws-amplify-react instead for now.

thheller 2021-02-22T08:29:32.005400Z

@barnes.kai did you make sure to load the webpack output before the CLJS output? as described in the blogpost that is your responsibility and the error you get suggest you missed that.

Kai 2021-02-22T08:40:35.005600Z

Ah sorry. I had swapped them when checking another error and had forgotten to swap them back. With the scripts in the correct order I get some other errors about React not being provided

thheller 2021-02-22T08:52:11.006Z

well did you run webpack to provide those dependencies? you can't just include the external index directly?

Kai 2021-02-22T09:38:21.006400Z

I’ve basically just followed the example from https://code.thheller.com/blog/shadow-cljs/2020/05/08/how-about-webpack-now.html#option-2-js-provider-external and have run npx webpack to generate the libs.js file. Does this not include the dependencies then?

thheller 2021-02-22T09:41:36.007100Z

if you are loading that output it should

thheller 2021-02-22T09:41:55.007300Z

but I don't know how you are using things so there is likely just a small mistake somewhere in your setup

Kai 2021-02-22T09:43:56.007800Z

Do you have/know of any examples of using webpack with this sort of set up that I could refer to?

thheller 2021-02-22T09:50:10.008Z

there is literally nothing else you need to do than described in my post. so the problem is likely somewhere else.

thheller 2021-02-22T09:50:26.008200Z

basically you run shadow-cljs watch app or so. once that is done you run webpack

thheller 2021-02-22T09:50:31.008400Z

then you load the page

thheller 2021-02-22T09:50:58.008600Z

you need to run webpack again whenever you add/remove npm requires in your code

thheller 2021-02-22T09:51:00.008800Z

but thats about it

thheller 2021-02-22T09:51:26.009Z

if you however execute steps in the wrong order or forget to run webpack again when you changed something you get those errors

Kai 2021-02-22T09:59:48.009200Z

Hmm okay. I’ll check over everything again and give it another try tomorrow. Thanks for your help looking into it anyway!

tomrbowden 2021-02-22T10:32:06.009400Z

@barnes.kai I’ve also been struggling with this one today. I’ve gotten it to work, but there are some big concerns regarding the production bundle size libs.js (1.66 MiB)

tomrbowden 2021-02-22T10:33:17.009600Z

As for webpack, I added a webpack.config.js file to the base level of the project:

const path = require('path');

module.exports = {
  entry: './target/index.js',
  output: {
    filename: 'libs.js',
    path: path.resolve(__dirname, 'public', 'js'),
  },
  module: {
    rules: [
      {
        test: /\.m?js/,
        resolve: {
            fullySpecified: false
        }
      }
    ]
  },
  resolve: {
    fallback: {
      "crypto": require.resolve("crypto-browserify"),
      "stream": require.resolve("stream-browserify")
    }
  }
};

tomrbowden 2021-02-22T10:34:01.009800Z

You will need to install “crypto-browserify” and “stream-browserify” as dev dependencies

tomrbowden 2021-02-22T10:34:38.010Z

then run webpack with npx webpack --config webpack.config.js

tomrbowden 2021-02-22T10:35:36.010200Z

BTW, you will also need “webpack” and “webpack-cli” as dev dependencies. Why webpack is required as a dependency when we are using npx to run the webpack command is a mystery to me…

tomrbowden 2021-02-22T10:36:21.010400Z

My CLJS file looks like this:

(ns aws-auth.app.core
  (:require [reagent.dom :as rdom]
            ["react" :as react]
            ["react-dom" :as react-dom]
            ["aws-amplify" :default Amplify]
            ["@aws-amplify/ui-react" :refer [AmplifyAuthenticator AmplifySignOut]]
            ["./aws-exports.js" :default awsconfig]))

(defn app []
  [:> AmplifyAuthenticator
   [:div
    [:h1 "My app"]]
   [:> AmplifySignOut]])

(defn render []
  (rdom/render [app] (.getElementById js/document "root")))

(defn ^:export main []
  (.configure Amplify awsconfig)
  (render))

tomrbowden 2021-02-22T10:37:58.010600Z

“react” and “react-dom” are needed as require’s here otherwise Shadow-CLJS will not include them as external JS files

tomrbowden 2021-02-22T10:39:20.010800Z

@thheller Is it possible to include just a few external JS libraries this way, and have Shadow-CLJS bundle the rest (with optimizations)?

thheller 2021-02-22T10:52:56.011Z

@tom.bowden webpack is actually better at bundling npm dependencies so the overall result should be smaller when using webpack. you do however need to the set the production mode for webpack. not sure which setting this is though

tomrbowden 2021-02-22T11:07:15.011200Z

Webpack defaults to ‘production’: “The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value.”

tomrbowden 2021-02-22T11:09:32.011400Z

I’m not sure why the libs.js bundle is as large as it is…

thheller 2021-02-22T11:14:52.011600Z

it would be the same size or larger in shadow-cljs

thheller 2021-02-22T11:14:59.011800Z

amplify is a gigantic package

tomrbowden 2021-02-22T11:16:02.012Z

But wouldn’t a production build do a huge amount of dead-code elimination with Google Closure?

tomrbowden 2021-02-22T11:17:20.012200Z

Even when adding:

mode: 'production',
  optimization: {
    usedExports: true,
  },
to the webpack.config.js for webpack tree shaking, no effect on the size of libs.js (still 1.66MiB)

juhoteperi 2021-02-22T11:19:13.012400Z

Closure doesn't really work that well for optimizing JS code not written for Closure (Cljs generates code for it), so it doesn't work for many libraries. Shadow-cljs doesn't use it by default for JS packages: https://shadow-cljs.github.io/docs/UsersGuide.html#js-provider

tomrbowden 2021-02-22T11:20:55.012600Z

I see. 1.6MB+ is basically prohibitive for a client app, IMO. That makes me think twice about using Amplify…

thheller 2021-02-22T11:23:54.012800Z

don't forget about gzip. looks much less scary when gzipped typically

thheller 2021-02-22T11:24:09.013Z

but yeah the lib is rather large

thheller 2021-02-22T11:25:04.013200Z

it generally helps to only import what you actually need but I don't know if amplify is even setup that way

juhoteperi 2021-02-22T11:25:53.013400Z

Is that before gzip? Not optimal if you need all that before rendering anything, but also quite normal for any app using some bigger JS libraries. E.g. For app rendering maps 850KB for main module and 1.2MB for module including openlayers, proj4 and part of mapbox. Or another project main module 1.1MB and 900KB for module using Three.js and react-three-fiber.

tomrbowden 2021-02-22T11:33:14.013600Z

It was before gzip. After gzip it’s a more manageable 452 KB

tomrbowden 2021-02-22T11:33:20.013800Z

Phew!

rberger 2021-02-22T17:21:30.014100Z

Will try this out myself today. I love it when I wake up and the problem is solved. Thanks community! Amplify does have a way to break up what you include, but you still need to include a lot in most cases it seems…

Kai 2021-02-23T02:03:51.014300Z

Just tried @tom.bowden suggestions and it’s working for me now. Thanks!

juhoteperi 2021-02-21T11:11:37.004100Z

Might the new package uses ES6 modules instead of CJS, and you need to take this into account on :require forms, Shadow-cljs doc lists different require options for JS modules: https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages You cold use (js/console.log withAuthenticator) to check you are referring to the correct object.

Kai 2021-02-21T11:57:14.004300Z

I’ve already checked that unfortunately. (js/console.log withAuthenticator) prints the function as expected, so it seems like it’s being required correctly