Skip to content

Commit 69539d6

Browse files
committed
fix: 2FA setup and /tools/qr-code-generator crash on v5.2.1
v5.2.1 bumped vite ^7.3.1 -> ^8.0.7, which switched the default bundler from Rollup to Rolldown. Rolldown emits a different __toESM interop helper for default imports of CJS modules. With isNodeMode=1 (used for default imports), the helper unconditionally writes synth.default = wholeModule even when the source has its own `default` export, so: import QRCode from 'react-qr-code' resolves to the whole module object { __esModule, default: fn, QRCode: fn } instead of the component, and <QRCode /> ends up as createElement(<object>, ...) which throws React #130. The follow-up #418 hydration warning is React's recovery path remounting the tree. react-qr-code only ships a CJS entry (no `module:` or `exports:` field in its package.json), and its CJS entry is interop-CJS shaped: Object.defineProperty(exports, '__esModule', { value: true }); exports.QRCode = X; exports.default = X; Switching to the named import bypasses the broken interop because Rolldown does not emit __toESM for named CJS imports - the consumer just gets the module exports namespace and reads .QRCode directly. The lib's .d.ts only declares the default export, so there's a small module augmentation in app/types.d.ts to expose the existing-but-undeclared QRCode named export to TypeScript. Same fix is needed in web/app/routes/tools.qr-code-generator.tsx, which has the same import shape and crashes when typing into the input. Verified by inspecting the rebuilt user-settings chunk: - before: jsx(oi.default, { value }) with oi = __toESM(\$qrLib(), 1) - after: jsx(oi.QRCode, { value }) with oi = \$qrLib()
1 parent 108aa6d commit 69539d6

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

web/app/pages/UserSettings/components/TwoFA.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect } from 'react'
22
import { useTranslation } from 'react-i18next'
3-
import QRCode from 'react-qr-code'
3+
import { QRCode } from 'react-qr-code'
44
import { useFetcher } from 'react-router'
55
import { toast } from 'sonner'
66
import { LockIcon } from '@phosphor-icons/react'

web/app/routes/tools.qr-code-generator.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DownloadIcon } from '@phosphor-icons/react'
22
import { useState } from 'react'
3-
import QRCode from 'react-qr-code'
3+
import { QRCode } from 'react-qr-code'
44
import type { MetaFunction } from 'react-router'
55
import { redirect } from 'react-router'
66
import type { SitemapFunction } from 'remix-sitemap'

web/app/types.d.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Module augmentation for `react-qr-code`.
2+
//
3+
// The upstream package ships only a default export in its `.d.ts`, but the
4+
// CJS entry assigns the same component to `exports.QRCode` as well. Vite 8 /
5+
// Rolldown's `__toESM` helper produces a broken default-interop wrapper for
6+
// CJS modules whose source already declares `__esModule: true` and its own
7+
// `default` (the synthetic `.default` ends up pointing at the whole module
8+
// object instead of the component, so `<QRCode />` renders an object and
9+
// throws). Importing the existing-but-undeclared `QRCode` named export
10+
// sidesteps the wrapper.
11+
declare module 'react-qr-code' {
12+
import type { ComponentType, SVGProps, CSSProperties } from 'react'
13+
14+
export interface QRCodeProps extends SVGProps<SVGSVGElement> {
15+
value: string
16+
size?: number
17+
bgColor?: CSSProperties['backgroundColor']
18+
fgColor?: CSSProperties['color']
19+
level?: 'L' | 'M' | 'H' | 'Q'
20+
title?: string
21+
}
22+
23+
export const QRCode: ComponentType<QRCodeProps>
24+
}

0 commit comments

Comments
 (0)