Fork me on GitHub
#reagent
<
2021-02-21
>
Kai04:02:03

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?

juhoteperi11:02:37

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.

Kai11:02:14

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

rberger06:02:51

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 @URS3MMGGK 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.

Kai07:02:23

Thanks @U07GPFFAS, 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.

thheller08:02:32

@URS3MMGGK 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.

Kai08:02:35

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

thheller08:02:11

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

Kai09:02:21

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?

thheller09:02:36

if you are loading that output it should

thheller09:02:55

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

Kai09:02:56

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

thheller09:02:10

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

thheller09:02:26

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

thheller09:02:31

then you load the page

thheller09:02:58

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

thheller09:02:00

but thats about it

thheller09:02:26

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

Kai09:02:48

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

tomrbowden10:02:06

@URS3MMGGK 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)

tomrbowden10:02:17

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")
    }
  }
};

tomrbowden10:02:01

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

tomrbowden10:02:38

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

tomrbowden10:02:36

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…

tomrbowden10:02:21

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))

tomrbowden10:02:58

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

tomrbowden10:02:20

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

thheller10:02:56

@U0187JC6398 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

tomrbowden11:02:15

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

tomrbowden11:02:32

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

thheller11:02:52

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

thheller11:02:59

amplify is a gigantic package

tomrbowden11:02:02

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

tomrbowden11:02:20

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)

juhoteperi11:02:13

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

tomrbowden11:02:55

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

thheller11:02:54

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

thheller11:02:09

but yeah the lib is rather large

thheller11:02:04

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

juhoteperi11:02:53

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.

tomrbowden11:02:14

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

rberger17:02:30

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…

Kai02:02:51

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