Bug: MutationObserver error when deploying to Vercel
I'm getting a MutationObserver production error when using Lexical 0.14, I can't reproduce it on localhost and it fails only in production when I deploy to Vercel. This error goes away once I revert to Lexical 0.13
Error message: TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'
Example repo reproducing this issue: https://github.com/colin-jiang/lexical-mutationobserver-bug
Steps To Reproduce
- Download example repo and npm run dev, home page should properly render with richtexteditor input
- Deploy to Vercel: https://lexical-mutationobserver-bug.vercel.app/
- Home page crashes with client side error due to MutationObserver issue, error in console
Dependencies listed in package.json Next.js page router and Next.js version 12.0.9.
The current behavior
Lexical editor throws error on load when deployed, no error on localhost
The expected behavior
No error
As far as I can tell this looks like a bug in next.js, the minifier is doing an incorrect rename
Here's a segment of the production code that's broken:
minified commitPendingUpdates
function so(t, e) {
const n = t._pendingEditorState,
r = t._rootElement,
i = t._headless || null === r;
if (null === n) return;
const s = t._editorState,
l = s._selection,
c = n._selection,
a = 0 !== t._dirtyType,
u = qr,
d = Jr,
f = Ur,
h = t._updating,
g = t._observer;
let _ = null;
if (
((t._pendingEditorState = null),
(t._editorState = n),
!i && a && null !== g)
) {
(Ur = t), (qr = n), (Jr = !1), (t._updating = !0);
try {
const e = t._dirtyType,
r = t._dirtyElements,
o = t._dirtyLeaves;
g.disconnect(),
(_ = (function (t, e, n, r, o, i) {
(Xe = ""),
(Qe = ""),
(Ye = ""),
(tn = 2 === r),
(nn = null),
(Ke = n),
(Be = n._config),
(Ve = n._nodes),
(je = Ke._listeners.mutation),
(qe = o),
(Ue = i),
(Je = t._nodeMap),
($e = e._nodeMap),
(en = e._readOnly),
(Ge = new Map(n._keyToDOMMap));
const s = new Map();
return (
(He = s),
_n("root", null),
(Ke = void 0),
(Ve = void 0),
(qe = void 0),
(Ue = void 0),
(Je = void 0),
($e = void 0),
(Be = void 0),
(Ge = void 0),
(He = void 0),
s
);
})(s, n, t, e, r, o));
} catch (o) {
if ((o instanceof Error && t._onError(o), $r)) throw o;
return (
Po(t, null, r, n),
Pt(t),
(t._dirtyType = 2),
($r = !0),
so(t, s),
void ($r = !1)
);
} finally {
g.observe(o, Hr), (t._updating = h), (qr = u), (Jr = d), (Ur = f);
}
// removed code here
}
// removed code here
}
Here are just the relevant parts:
function so(t, e) {
const r = t._rootElement;
if (…) {
try {
const o = t._dirtyLeaves;
} catch (o) {
return;
} finally {
g.observe(o, Hr);
}
}
}
This is the original (only relevant parts):
export function commitPendingUpdates(
editor: LexicalEditor,
recoveryEditorState?: EditorState,
): void {
const rootElement = editor._rootElement;
if (…) {
try {
const dirtyLeaves = editor._dirtyLeaves;
} catch (error) {
return;
} finally {
observer.observe(rootElement as Node, observerOptions);
}
}
}
It looks like there's an unsafe and incorrect renaming in the production output. Maybe upgrading next.js would help fix this, I don't think there's anything that can be done in lexical to work around it.
This is what lexical's prod build looks like, which would be what next.js's minifier is using as input and explains why next.js got this wrong in this specific way.
function Vi(t, n) {
const i = t._rootElement;
if (...) {
try {
const i = t._dirtyLeaves;
} catch (e) {
return;
} finally {
g.observe(i, Ii);
}
}
}
A correct minifier implementation would realize that the try {} and finally {} do not have the same scope.
You can try it yourself in any JavaScript implementation:
(() => { const i = 'outer'; try { const i = 'inner'; } finally { console.log(i); } })()
outer
If anyone else is seeing this: I've heard reports that the swc minifier does not have this bug, and it's the default minifier in the latest version of next.js.
If anyone else is seeing this: I've heard reports that the swc minifier does not have this bug, and it's the default minifier in the latest version of next.js.
I can confirm, been using TerserPlugin for minification, after switching to swcMinify mode the problem is gone
I can see this issue even in 0.16.1
The bug is in next.js, not lexical, so the lexical version is not so relevant.
For non Next.js projects. This problem also exists in older webpack versions. When updating to the latest v5.93.0 this problem is resolved. So TerserPlugin has addressed this issue which is part of webpack.
For non Next.js projects. This problem also exists in older webpack versions. When updating to the latest v5.93.0 this problem is resolved. So TerserPlugin has addressed this issue which is part of webpack.
if i read your comment early, I will save my 2 days.