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?
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.
I’ve already checked that unfortunately. (js/console.log withAuthenticator)
prints the function as expected, so it seems like it’s being required correctly
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.
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.
@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.
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
well did you run webpack to provide those dependencies? you can't just include the external index directly?
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?
if you are loading that output it should
but I don't know how you are using things so there is likely just a small mistake somewhere in your setup
Do you have/know of any examples of using webpack with this sort of set up that I could refer to?
there is literally nothing else you need to do than described in my post. so the problem is likely somewhere else.
basically you run shadow-cljs watch app
or so. once that is done you run webpack
then you load the page
you need to run webpack again whenever you add/remove npm requires in your code
but thats about it
if you however execute steps in the wrong order or forget to run webpack again when you changed something you get those errors
Hmm okay. I’ll check over everything again and give it another try tomorrow. Thanks for your help looking into it anyway!
@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)
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")
}
}
};
You will need to install “crypto-browserify” and “stream-browserify” as dev dependencies
then run webpack with npx webpack --config webpack.config.js
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…
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))
“react” and “react-dom” are needed as require’s here otherwise Shadow-CLJS will not include them as external JS files
@thheller Is it possible to include just a few external JS libraries this way, and have Shadow-CLJS bundle the rest (with optimizations)?
@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
Webpack defaults to ‘production’: “The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value.”
I’m not sure why the libs.js bundle is as large as it is…
it would be the same size or larger in shadow-cljs
amplify is a gigantic package
But wouldn’t a production build do a huge amount of dead-code elimination with Google Closure?
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)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
I see. 1.6MB+ is basically prohibitive for a client app, IMO. That makes me think twice about using Amplify…
don't forget about gzip. looks much less scary when gzipped typically
but yeah the lib is rather large
it generally helps to only import what you actually need but I don't know if amplify is even setup that way
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.
It was before gzip. After gzip it’s a more manageable 452 KB
Phew!
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…
Just tried @tom.bowden suggestions and it’s working for me now. Thanks!