124 lines
4 KiB
TypeScript
124 lines
4 KiB
TypeScript
import { logger } from './logger';
|
|
|
|
/**
|
|
* toast - Wrapper lazy pour react-hot-toast
|
|
*
|
|
* CRITICAL FIX: Charge react-hot-toast de manière lazy pour éviter
|
|
* les collisions de noms de variables (ie) lors de la minification.
|
|
*
|
|
* Le problème: react-hot-toast utilise une variable 'ie' qui entre
|
|
* en conflit avec d'autres variables minifiées dans le même chunk.
|
|
* Solution: Chargement dynamique pour isoler le module.
|
|
*
|
|
* Le module est préchargé dans main.tsx AVANT le rendu de l'app,
|
|
* donc il sera toujours disponible quand ce wrapper est utilisé.
|
|
*
|
|
* Usage:
|
|
* import toast from '@/utils/toast';
|
|
* toast.success('Message');
|
|
* toast.error('Error');
|
|
*/
|
|
|
|
// Import dynamique - cela force Vite à créer un chunk séparé
|
|
// Le module est préchargé dans main.tsx, donc il sera déjà chargé
|
|
// quand ce fichier est évalué
|
|
const toastModulePromise = import('react-hot-toast');
|
|
|
|
type ToastDefault = typeof import('react-hot-toast').default;
|
|
type ToastModule = { default: ToastDefault };
|
|
|
|
// Stub API shape used when module fails or is not yet loaded
|
|
interface ToastStub {
|
|
success: (...args: unknown[]) => void;
|
|
error: (...args: unknown[]) => void;
|
|
loading: (...args: unknown[]) => void;
|
|
custom: (...args: unknown[]) => unknown;
|
|
dismiss: (...args: unknown[]) => void;
|
|
remove: (...args: unknown[]) => void;
|
|
promise: (...args: unknown[]) => Promise<unknown>;
|
|
}
|
|
|
|
// Cache pour le module une fois chargé
|
|
let toastModule: ToastModule | null = null;
|
|
let isResolved = false;
|
|
|
|
// Charger le module et le mettre en cache immédiatement
|
|
toastModulePromise.then((mod) => {
|
|
toastModule = mod;
|
|
isResolved = true;
|
|
}).catch(() => {
|
|
isResolved = true;
|
|
// Ignorer les erreurs de chargement
|
|
});
|
|
|
|
const createNoopStub = (): ToastStub => ({
|
|
success: () => {},
|
|
error: () => {},
|
|
loading: () => {},
|
|
custom: () => undefined,
|
|
dismiss: () => {},
|
|
remove: () => {},
|
|
promise: () => Promise.resolve(),
|
|
});
|
|
|
|
const createDeferredStub = (): ToastStub => ({
|
|
success: (...args: unknown[]) => {
|
|
toastModulePromise.then((mod) => (mod.default.success as (...a: unknown[]) => void)(...args));
|
|
},
|
|
error: (...args: unknown[]) => {
|
|
toastModulePromise.then((mod) => (mod.default.error as (...a: unknown[]) => void)(...args));
|
|
},
|
|
loading: (...args: unknown[]) => {
|
|
toastModulePromise.then((mod) => (mod.default.loading as (...a: unknown[]) => void)(...args));
|
|
},
|
|
custom: (...args: unknown[]) =>
|
|
toastModulePromise.then((mod) => (mod.default.custom as (...a: unknown[]) => unknown)(...args)),
|
|
dismiss: (...args: unknown[]) => {
|
|
toastModulePromise.then((mod) => (mod.default.dismiss as (...a: unknown[]) => void)(...args));
|
|
},
|
|
remove: (...args: unknown[]) => {
|
|
toastModulePromise.then((mod) => (mod.default.remove as (...a: unknown[]) => void)(...args));
|
|
},
|
|
promise: (...args: unknown[]) =>
|
|
toastModulePromise.then((mod) => (mod.default.promise as (...a: unknown[]) => Promise<unknown>)(...args)),
|
|
});
|
|
|
|
/**
|
|
* Récupère le module toast de manière synchrone
|
|
* Le module devrait être déjà chargé grâce au préchargement dans main.tsx
|
|
*/
|
|
function getToastModuleSync(): ToastDefault | ToastStub {
|
|
if (!toastModule && isResolved) {
|
|
logger.error('Toast module failed to load');
|
|
return createNoopStub();
|
|
}
|
|
|
|
if (toastModule) {
|
|
return toastModule.default;
|
|
}
|
|
|
|
return createDeferredStub();
|
|
}
|
|
|
|
// Créer un proxy qui délègue toutes les méthodes à react-hot-toast
|
|
const toast = new Proxy({} as ToastDefault, {
|
|
get(_target, prop) {
|
|
const toastFn = getToastModuleSync();
|
|
const method = (toastFn as Record<string, unknown>)[prop as string];
|
|
if (typeof method === 'function') {
|
|
return method.bind(toastFn);
|
|
}
|
|
return method;
|
|
},
|
|
apply(_target, _thisArg, args: unknown[]) {
|
|
const toastFn = getToastModuleSync();
|
|
if (typeof toastFn === 'function') {
|
|
return (toastFn as (...a: unknown[]) => unknown)(...args);
|
|
}
|
|
return toastModulePromise.then((mod) =>
|
|
(mod.default as (...a: unknown[]) => unknown)(...args)
|
|
);
|
|
},
|
|
}) as ToastDefault;
|
|
|
|
export default toast;
|