processInitQueue.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import {createRoot} from 'react-dom/client';
  2. import throttle from 'lodash/throttle';
  3. import {exportedGlobals} from 'sentry/bootstrap/exportGlobals';
  4. import type {OnSentryInitConfiguration} from 'sentry/types/system';
  5. import {SentryInitRenderReactComponent} from 'sentry/types/system';
  6. import {renderDom} from './renderDom';
  7. import {renderOnDomReady} from './renderOnDomReady';
  8. const COMPONENT_MAP = {
  9. [SentryInitRenderReactComponent.INDICATORS]: () =>
  10. import(/* webpackChunkName: "Indicators" */ 'sentry/components/indicators'),
  11. [SentryInitRenderReactComponent.SYSTEM_ALERTS]: () =>
  12. import(/* webpackChunkName: "SystemAlerts" */ 'sentry/views/app/systemAlerts'),
  13. [SentryInitRenderReactComponent.SETUP_WIZARD]: () =>
  14. import(/* webpackChunkName: "SetupWizard" */ 'sentry/views/setupWizard'),
  15. [SentryInitRenderReactComponent.U2F_SIGN]: () =>
  16. import(/* webpackChunkName: "U2fSign" */ 'sentry/components/u2f/u2fsign'),
  17. [SentryInitRenderReactComponent.SU_STAFF_ACCESS_FORM]: () =>
  18. import(
  19. /* webpackChunkName: "SuperuserStaffAccessForm" */ 'sentry/components/superuserStaffAccessForm'
  20. ),
  21. };
  22. async function processItem(initConfig: OnSentryInitConfiguration) {
  23. /**
  24. * Allows our auth pages to dynamically attach a client side password
  25. * strength indicator The password strength component is very
  26. * heavyweight as it includes the zxcvbn, a relatively byte-heavy
  27. * password strength estimation library. Load it on demand.
  28. */
  29. if (initConfig.name === 'passwordStrength') {
  30. const {input, element} = initConfig;
  31. if (!input || !element) {
  32. return;
  33. }
  34. const inputElem = document.querySelector(input);
  35. const rootEl = document.querySelector(element);
  36. if (!inputElem || !rootEl) {
  37. return;
  38. }
  39. const {PasswordStrength} = await import(
  40. /* webpackChunkName: "PasswordStrength" */ 'sentry/components/passwordStrength'
  41. );
  42. const root = createRoot(rootEl);
  43. inputElem.addEventListener(
  44. 'input',
  45. throttle(e => {
  46. root.render(<PasswordStrength value={e.target.value} />);
  47. })
  48. );
  49. return;
  50. }
  51. /**
  52. * Allows server rendered templates to render a React component to DOM
  53. * without exposing the component globally.
  54. */
  55. if (initConfig.name === 'renderReact') {
  56. if (!COMPONENT_MAP.hasOwnProperty(initConfig.component)) {
  57. return;
  58. }
  59. const {default: Component} = await COMPONENT_MAP[initConfig.component]();
  60. renderOnDomReady(() =>
  61. // TODO(ts): Unsure how to type this, complains about u2fsign's required props
  62. renderDom(Component as any, initConfig.container, initConfig.props)
  63. );
  64. }
  65. /**
  66. * Callback for when js bundle is loaded. Provide library + component references
  67. * for downstream consumers to use.
  68. */
  69. if (initConfig.name === 'onReady' && typeof initConfig.onReady === 'function') {
  70. initConfig.onReady(exportedGlobals);
  71. }
  72. }
  73. /**
  74. * This allows server templates to push "tasks" to be run after application has initialized.
  75. * The global `window.__onSentryInit` is used for this.
  76. *
  77. * Be careful here as we can not guarantee type safety on `__onSentryInit` as
  78. * these will be defined in server rendered templates
  79. */
  80. export async function processInitQueue() {
  81. // Currently, this is run *before* anything is queued in
  82. // `window.__onSentryInit`. We want to provide a migration path for potential
  83. // custom plugins that rely on `window.SentryApp` so they can start migrating
  84. // their plugins ASAP, as `SentryApp` will be loaded async and will require
  85. // callbacks to access it, instead of via `window` global.
  86. if (
  87. typeof window.__onSentryInit !== 'undefined' &&
  88. !Array.isArray(window.__onSentryInit)
  89. ) {
  90. return;
  91. }
  92. const queued = window.__onSentryInit;
  93. // Stub future calls of `window.__onSentryInit.push` so that it is
  94. // processed immediately (since bundle is loaded at this point and no
  95. // longer needs to act as a queue)
  96. //
  97. window.__onSentryInit = {
  98. push: processItem,
  99. };
  100. if (Array.isArray(queued)) {
  101. // These are all side-effects, so no need to return a value, but allow consumer to
  102. // wait for all initialization to finish
  103. await Promise.all(queued.map(processItem));
  104. }
  105. }