interopDefault.ts 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. /**
  2. * Unwrap a default import that a bundler's CommonJS->ESM interop may have
  3. * wrapped in a module namespace object.
  4. *
  5. * Some CommonJS packages set `module.exports = { default: X, Named: X }`.
  6. * Depending on the bundler (and differing between the browser build, the test
  7. * runner, and Node's own ESM loader), `import X from 'pkg'` can hand you that
  8. * whole object instead of `X`. Rendering such an object as a React component
  9. * throws "Element type is invalid ... got: object" (React error #130) — see
  10. * #2616, where react-simple-keyboard's default import arrived as the namespace
  11. * object and crashed every SpoolBuddy screen on input focus.
  12. *
  13. * This returns the value unchanged when it is already a usable React element
  14. * type (a function/class component, a tag string, or an object carrying a React
  15. * `$$typeof` marker such as forwardRef/memo/lazy). Otherwise it tries `.default`
  16. * and then each of `fallbackKeys` in order, returning the first usable one, and
  17. * finally falls back to the original value.
  18. */
  19. export function resolveInteropDefault<T = unknown>(value: unknown, fallbackKeys: string[] = []): T {
  20. if (isRenderableType(value)) return value as T;
  21. if (value !== null && typeof value === 'object') {
  22. const obj = value as Record<string, unknown>;
  23. if (isRenderableType(obj.default)) return obj.default as T;
  24. for (const key of fallbackKeys) {
  25. if (isRenderableType(obj[key])) return obj[key] as T;
  26. }
  27. }
  28. return value as T;
  29. }
  30. /** True when `v` is something React can render as an element type. */
  31. function isRenderableType(v: unknown): boolean {
  32. if (typeof v === 'function' || typeof v === 'string') return true;
  33. // forwardRef / memo / lazy / context objects are valid element types and are
  34. // distinguished from a plain interop wrapper by their React `$$typeof` marker.
  35. return typeof v === 'object' && v !== null && '$$typeof' in v;
  36. }