Closed afk-mario closed 2 years ago
Please post the error stack if possible; it might hint us what's going on. Feel free to edit-out the paths/sensitive data. I would like to help but I'm in a peculiar situation: you admit it can't be recreated on a fresh install and I don't have access to your project so I can't recreate...
Sure let me know how else I can help to track this down
Uncaught Error: Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.
Check this link for more details:
https://remix.run/pages/gotchas#server-code-in-client-bundles
invariant2 invariant.js:13
RemixRoute components.js:176
React 16
renderWithHooks
mountIndeterminateComponent
beginWork
callCallback2
invokeGuardedCallbackDev
invokeGuardedCallback
beginWork$1
performUnitOfWork
workLoopSync
renderRootSync
performSyncWorkOnRoot
flushSyncCallbacks
flushSync
legacyCreateRootFromDOMContainer
legacyRenderSubtreeIntoContainer
hydrate
<anonymous> entry.client.jsx:4
invariant.js:13:10
invariant2 invariant.js:13
RemixRoute components.js:176
React 11
renderWithHooks
mountIndeterminateComponent
beginWork
callCallback2
invokeGuardedCallbackDev
invokeGuardedCallback
beginWork$1
performUnitOfWork
workLoopSync
renderRootSync
performSyncWorkOnRoot
performSyncWorkOnRoot self-hosted:1168
React 5
flushSyncCallbacks
flushSync
legacyCreateRootFromDOMContainer
legacyRenderSubtreeIntoContainer
hydrate
<anonymous> entry.client.jsx:4
InnerModuleEvaluation self-hosted:2331
evaluation self-hosted:2292
invariant2@http://localhost:3000/build/_shared/chunk-A62FEDJL.js:1146:11
RemixRoute@http://localhost:3000/build/_shared/chunk-A62FEDJL.js:2636:13
renderWithHooks@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:11532:35
mountIndeterminateComponent@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:14773:21
beginWork@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:15695:22
beginWork$1@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:18985:22
performUnitOfWork@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:18436:20
workLoopSync@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:18372:30
renderRootSync@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:18351:15
performSyncWorkOnRoot@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:18110:42
flushSyncCallbacks@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:8645:30
flushSync@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:18194:15
legacyCreateRootFromDOMContainer@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:20473:13
legacyRenderSubtreeIntoContainer@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:20513:21
hydrate@http://localhost:3000/build/_shared/chunk-BHAU3OQB.js:20561:18
@http://localhost:3000/build/entry.client-F52PTIBS.js:20:30
Check the https://remix.run/docs/en/v1/pages/gotchas#server-code-in-client-bundles — something within invariant2
does not suit the server bundle and is client-only. Sorry for basic hint, I can't advise more without seeing real code. But read the gotchas chapter, either move statements from global scope to actions, or use typeof window
checks...
How did it go? Any luck troubleshooting?
@afk-mario I have encountered the same issue and the problem was having statements outside actions, something @revelt warned against in his comment.
So, this will error:
const foo = process.env.FOO; // statement in the global scope!
export const action: ActionFunction = async () => {
console.log(foo).
...
};
...while this won't:
export const action: ActionFunction = async () => {
const foo = process.env.FOO;
console.log(foo).
...
};
Thanks for the pointers @revelt @ibreslauer I'm gonna spend today debugging this and come back with more info later
I have isolated the issue to reach-dialog
not sure what's going on.
I have tried rendering an empty reach dialog and this happens, I tried to render the same children outside of the dialog and the error doesn't happen.
Also this worked fine until I added a react-icon
as stated before
This is my Dialog component:
import PropTypes from "prop-types";
import classNames from "classnames";
import { DialogOverlay, DialogContent } from "@reach/dialog";
import Header from "./header";
import Content from "./content";
import Footer from "./footer";
import { statuses, themes, classNamePrefix } from "./constants";
/**
* @param {Object} props
* @param {string} [props.className=] - Custom class name
* @param {string} [props.theme=light] - [light, dark]
* @param {string} [props.status=normal] - [normal, danger]
* @param {Object} [props.isOpen=] - Boolean to open and close the modal
* @param {Object} [props.onDismiss=] - What will happen when the user press ESC (close the modal)
* @param {Object} [props.dangerouslyBypassFocusLock=] - Should be false always, unless you know what you are doing
*/
function Modal({
theme,
status,
isOpen,
onDismiss,
children,
className,
...rest
}) {
const customClassName = classNames(`${classNamePrefix}-container`, className);
return (
<DialogOverlay
className={`${classNamePrefix}-backdrop`}
isOpen={isOpen}
onDismiss={onDismiss}
data-theme={theme}
data-status={status}
>
<DialogContent className={customClassName} {...rest}>
{children}
</DialogContent>
</DialogOverlay>
);
}
Modal.propTypes = {
status: PropTypes.oneOf(statuses),
theme: PropTypes.oneOf(themes),
className: PropTypes.string,
children: PropTypes.node.isRequired,
isOpen: PropTypes.bool,
onDismiss: PropTypes.func.isRequired,
};
Modal.defaultProps = {
className: null,
status: "normal",
theme: "light",
isOpen: false,
};
Modal.Header = Header;
Modal.Content = Content;
Modal.Footer = Footer;
export default Modal;
I have been debugging this for the last couple of days. I figured out that the issue happens only when two specific component share the same Dialog component.
I still don't understand how the modal logic + a react-icon in a different file = to server code leaking to the client.
I'm going to try to explain the components.
Component A
is a Modal component in charge of letting the user login and it's inside my layout that it's applied to any root route.
It looks something like this:
// page.jsx
function Page({ className, children }) {
const customClassName = classnames("page", className);
const isConnected = useIsConnected();
return (
<>
<SiteHeader>
{isConnected ? <User className="site-header-action" /> : null}
{!isConnected ? <ConnectModal className="site-header-action" /> : null}
</SiteHeader>
<main className={customClassName}>{children}</main>
</>
);
}
// connect-modal.jsx
import { useState, useCallback, useEffect } from "react";
import { CgEthernet } from "react-icons/cg";
import { getEnv } from "~/utils/env";
import { useisConnected } from "~/hooks/use-is-connected";
import Modal from "~/components/modal";
import LoginForm from "~/containers/login-form";
function ConnectModal({ className }) {
const { REMIX_APP_NETWORK_ID: networkId } = getEnv();
const [isOpen, setIsOpen] = useState();
const open = useCallback(() => setIsOpen(true), [setIsOpen]);
const close = useCallback(() => setIsOpen(false), [setIsOpen]);
const isConnected = useisConnected();
useEffect(() => {
if (isConnected && isOpen) {
close();
}
}, [isConnected, isOpen, close]);
return (
<>
<button
className={`connect-modal-button ${className}`}
type="button"
onClick={open}
>
Connect
</button>
<Modal
isOpen={isOpen}
onDismiss={close}
aria-label="Connect Modal"
theme="dark"
>
<Modal.Header
title="Connect"
onDismiss={close}
icon={CgEthernet}
/>
<LoginForm networkId={networkId} />
<Modal.Footer>
<button className="button" type="button" onClick={close}>
Close
</button>
</Modal.Footer>
</Modal>
</>
);
}
export default ConnectModal;
And the Component B
is a modal component in charge of deleting items from a DB
// product-delete-modal.jsx
import { useFetcher } from "@remix-run/react";
import { useEffect } from "react";
import { CgTrash } from "react-icons/cg";
import Modal from "~/components/modal";
function ProductDeleteModal({ id, name, isOpen, onDismiss }) {
const fetcher = useFetcher();
const mutate = () => {
fetcher.submit(null, {
method: "post",
action: `/admin/product/${id}/delete`,
});
};
useEffect(() => {
if (fetcher.type === "done" && fetcher.data.ok) {
onDismiss();
}
}, [fetcher, onDismiss]);
return (
<Modal
isOpen={isOpen}
onDismiss={onDismiss}
aria-label="Product Delete modal"
theme="dark"
>
<Modal.Header title={`Delete ${name}`} icon={CgTrash} />
<p>Are you sure you want to delete {name}?</p>
<p>
This action <strong>cannot</strong> be undone.
</p>
<Modal.Footer>
<button className="button" type="button" onClick={onDismiss}>
Cancel
</button>
<button
className="button"
type="button"
onClick={mutate}
disabled={fetcher.state !== "idle"}
>
{fetcher.state !== "idle" ? "..." : "Delete"}
</button>
</Modal.Footer>
</Modal>
);
}
export default ProductDeleteModal;
My current solution was to use my normal Modal
component for the ComponentA
and a AlertDialog
component for the ComponentB
.
After this I was able to add the react-icon
component in my other components.
I guess there is something in the logic of the connect modal or the delete product modal that's is causing the leakage but the current error message is not really helpful.
What is in this file?
import { getEnv } from "~/utils/env";
The error message is pretty descriptive and is likely calling out the root cause.
This normally occurs when you have server code in your client modules.
You are likely calling a node API somewhere in your client code. The ‘getEnv’ seems suspicious. You might want to try renaming the file to ‘env.server.ts’ to tell Remix not to include it in the client bundle.
The error message is pretty descriptive and is likely calling out the root cause.
I think it would be helpful to get the function or code that it's getting leaked from the back-end.
You are likely calling a node API somewhere in your client code. The ‘getEnv’ seems suspicious. You might want to try renaming the file to ‘env.server.ts’ to tell Remix not to include it in the client bundle.
Was my first suspicion as well, removed all the getEnv calls and still got the error.
What I don't understand is how a SVG icon component is related to leaked front-end/back-end code
It would be a very opportunistic shortcut, but what if you took the SVG source of the icon, placed it into your ad-hoc component, and then attributed the icon creators somewhere? I'm just introducing a possible option — that's what I'd do if I were stuck — I'd lift the raw SVG and mention it somewhere in root readme and call it a day?
I'm triggering this error by adding the class, hover:cursor-pointer
to a <span>
. Removing the class resumes running. When running dev. No crash in the built app.
Error: Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.
Check this link for more details:
https://remix.run/pages/gotchas#server-code-in-client-bundles
at invariant2 (http://localhost:3000/build/_shared/chunk-YBAZ5Y5H.js:1119:11)
at RemixRoute (http://localhost:3000/build/_shared/chunk-YBAZ5Y5H.js:2595:3)
at renderWithHooks (http://localhost:3000/build/entry.client-TF6QMZ3N.js:11070:26)
at mountIndeterminateComponent (http://localhost:3000/build/entry.client-TF6QMZ3N.js:13190:21)
at beginWork (http://localhost:3000/build/entry.client-TF6QMZ3N.js:13977:22)
at HTMLUnknownElement.callCallback2 (http://localhost:3000/build/entry.client-TF6QMZ3N.js:3680:22)
at Object.invokeGuardedCallbackDev (http://localhost:3000/build/entry.client-TF6QMZ3N.js:3705:24)
at invokeGuardedCallback (http://localhost:3000/build/entry.client-TF6QMZ3N.js:3739:39)
at beginWork$1 (http://localhost:3000/build/entry.client-TF6QMZ3N.js:17086:15)
at performUnitOfWork (http://localhost:3000/build/entry.client-TF6QMZ3N.js:16314:20)
Making almost any change makes it work again. Even as simple as adding a comment //
in the component or file.
Update: In this case, it ended up being a cache name collision in the browser. The file chunk-L6QOVPVL.js
had been something else before, and the browser was still caching it. Toggling "Disable cache" in the developer tools and refreshing resolved the issue.
Also experiencing this issue, the problem is that nobody in the team can reproduce it except random occurrences between deploys to prod. It works just fine after refreshing. Our suspect is cache conflict as well, but what concerns me is why this error leaks from the ErrorBoundary
components exported on all levels from Root to individual routes?
Edit: we use Cloudflare cache
I think this is related to #2987 as I'm getting that error after updating
It is related to #2987 ! I minified the code and it's working now!
Closed in favor of #2987
For me this happens if I do this:
import { db } from "../../../common/src/utils/db.server";
But everything works if I do this:
import { db } from "~/utils/db.server";`
The db.server.ts
file is from the Jokes tutorial app.
What version of Remix are you using?
1.4.3
Steps to Reproduce
Can't reproduce on a clean install but happens whenever I use a ReactIcon component, nothing else changes in the application.
Expected Behavior
To render the application without server code
Actual Behavior
The app crashes