@servicetitan/web-components 36.3.1 → 37.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/anvil1-providers.d.ts +6 -0
  2. package/dist/anvil1-providers.d.ts.map +1 -0
  3. package/dist/anvil1-providers.js +23 -0
  4. package/dist/anvil1-providers.js.map +1 -0
  5. package/dist/anvil2-providers.d.ts +5 -0
  6. package/dist/anvil2-providers.d.ts.map +1 -0
  7. package/dist/anvil2-providers.js +13 -0
  8. package/dist/anvil2-providers.js.map +1 -0
  9. package/dist/css-injector/index.d.ts +3 -0
  10. package/dist/css-injector/index.d.ts.map +1 -0
  11. package/dist/css-injector/index.js +4 -0
  12. package/dist/css-injector/index.js.map +1 -0
  13. package/dist/css-injector/mfe-style-registry.d.ts +14 -0
  14. package/dist/css-injector/mfe-style-registry.d.ts.map +1 -0
  15. package/dist/css-injector/mfe-style-registry.js +63 -0
  16. package/dist/css-injector/mfe-style-registry.js.map +1 -0
  17. package/dist/css-injector/with-css-injector.d.ts +6 -0
  18. package/dist/css-injector/with-css-injector.d.ts.map +1 -0
  19. package/dist/css-injector/with-css-injector.js +38 -0
  20. package/dist/css-injector/with-css-injector.js.map +1 -0
  21. package/dist/get-style-urls.d.ts +2 -0
  22. package/dist/get-style-urls.d.ts.map +1 -0
  23. package/dist/get-style-urls.js +21 -0
  24. package/dist/get-style-urls.js.map +1 -0
  25. package/dist/globals.d.ts +3 -3
  26. package/dist/globals.d.ts.map +1 -1
  27. package/dist/globals.js +19 -4
  28. package/dist/globals.js.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/register-headless.d.ts.map +1 -1
  34. package/dist/register-headless.js +1 -0
  35. package/dist/register-headless.js.map +1 -1
  36. package/dist/register.d.ts.map +1 -1
  37. package/dist/register.js +14 -55
  38. package/dist/register.js.map +1 -1
  39. package/dist/render.d.ts.map +1 -1
  40. package/dist/render.js +3 -11
  41. package/dist/render.js.map +1 -1
  42. package/dist/utils/get-bundle-info.d.ts.map +1 -1
  43. package/dist/utils/get-bundle-info.js +11 -4
  44. package/dist/utils/get-bundle-info.js.map +1 -1
  45. package/dist/utils/get-entry-points.d.ts +1 -0
  46. package/dist/utils/get-entry-points.d.ts.map +1 -1
  47. package/dist/utils/get-entry-points.js.map +1 -1
  48. package/dist/utils/get-ld-service-token.d.ts +2 -3
  49. package/dist/utils/get-ld-service-token.d.ts.map +1 -1
  50. package/dist/utils/get-ld-service-token.js +2 -7
  51. package/dist/utils/get-ld-service-token.js.map +1 -1
  52. package/package.json +36 -14
  53. package/src/__mocks__/register-anvil2-harness.tsx +49 -0
  54. package/src/__mocks__/test-component.tsx +9 -2
  55. package/src/__tests__/loader.test.tsx +32 -0
  56. package/src/__tests__/register-anvil2-optional.test.tsx +32 -0
  57. package/src/__tests__/register-anvil2.test.tsx +11 -64
  58. package/src/anvil1-providers.tsx +28 -0
  59. package/src/anvil2-providers.tsx +13 -0
  60. package/src/css-injector/__tests__/mfe-style-registry.test.ts +163 -0
  61. package/src/css-injector/__tests__/with-css-injector.test.tsx +128 -0
  62. package/src/css-injector/index.ts +2 -0
  63. package/src/css-injector/mfe-style-registry.ts +48 -0
  64. package/src/css-injector/with-css-injector.tsx +40 -0
  65. package/src/get-style-urls.ts +23 -0
  66. package/src/globals.ts +30 -10
  67. package/src/index.ts +1 -0
  68. package/src/register-headless.ts +1 -0
  69. package/src/register.tsx +15 -66
  70. package/src/render.ts +3 -14
  71. package/src/utils/get-bundle-info.ts +12 -5
  72. package/src/utils/get-entry-points.ts +1 -0
  73. package/src/utils/get-ld-service-token.ts +2 -10
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/register.tsx"],"sourcesContent":["import { type LDService } from '@servicetitan/launchdarkly-service';\nimport { Log } from '@servicetitan/log-service';\nimport { Provider } from '@servicetitan/react-ioc';\nimport { ExposedDependencies } from '@servicetitan/startup-utils';\nimport { ComponentType, FC, ReactElement, useEffect } from 'react';\nimport {\n BASENAME_TOKEN,\n Entries,\n EXPOSED_DEPENDENCIES_TOKEN,\n EXPOSED_INSTANCE_DEPENDENCIES_TOKEN,\n ExposedInstanceDependencies,\n IS_WEB_COMPONENT_TOKEN,\n IWebComponent,\n RENDER_ROOT_TOKEN,\n} from './common';\nimport { MFEDataContext } from './contexts/mfe-data-context';\nimport { MFEMetadataContext } from './contexts/mfe-metadata-context';\nimport { EVENT_BUS_TOKEN, EventBus } from './event-bus';\nimport { WEB_COMPONENT_NAME } from './globals';\nimport { HistoryManager } from './history-manager';\nimport { render } from './render';\nimport { getElementName, getLDServiceToken } from './utils';\n\nfunction createLink(url: string, onLoad: () => void) {\n const element = document.createElement('link');\n\n element.href = url;\n element.rel = 'stylesheet';\n element.crossOrigin = 'anonymous';\n element.onload = onLoad;\n element.onerror = onLoad;\n\n return element;\n}\n\nconst Placeholder: FC = () => <div style={{ display: 'none' }} />;\n\nexport interface RegisterOptions {\n legacyRoot?: boolean;\n sharedDependenciesNames?: string[];\n}\n\nexport function register(Component: ComponentType, light: boolean, options?: RegisterOptions) {\n class WebComponent extends HTMLElement implements IWebComponent {\n private root?: ShadowRoot;\n private portal?: ShadowRoot;\n private portalAnvilContainer?: HTMLDivElement;\n\n private styles: {\n total: number;\n loaded: number;\n elements: HTMLElement[];\n } = { total: 0, loaded: 0, elements: [] };\n\n private ldService?: LDService;\n private logService?: Log;\n private historyManager?: HistoryManager;\n private exposedDependencies?: ExposedDependencies;\n private exposedInstanceDependencies?: ExposedInstanceDependencies;\n private eventBus?: EventBus;\n private basename?: string;\n private onReady?: () => void;\n private onDispose?: () => void;\n private view?: ReturnType<typeof render>;\n\n connectedCallback() {\n /*\n * connectedCallback can be called when DOM element has moved\n * check that root is still connected to the DOM\n * if that's the case we don't need to reconnect MFE\n */\n if (this.root?.isConnected) {\n return;\n }\n const sharedDependenciesNames = options?.sharedDependenciesNames ?? [];\n const styleUrls = getStyleUrls(light, sharedDependenciesNames, this);\n\n this.styles = {\n total: styleUrls.length * 2,\n loaded: 0,\n elements: [],\n };\n\n this.root = this.shadowRoot ?? this.attachShadow({ mode: 'open' });\n /*\n * Portal shadow dom is created at the bottom of the body in order to be used as\n * the element for the design system components to attach modals and popups to.\n * This element is passed down the tree through context, and used within\n * the design system components.\n */\n const portalElement = document.createElement('div');\n portalElement.setAttribute(\n 'data-mfe-portal-name',\n getElementName({ WebComponent: WEB_COMPONENT_NAME! })\n );\n this.portal = document.body.appendChild(portalElement).attachShadow({ mode: 'open' });\n /*\n * Add element inside of portal so that Anvil 2 can attach modals and popups to it\n * since floating-ui does not support attaching straight to shadow dom\n */\n this.portalAnvilContainer = document.createElement('div');\n this.portal.appendChild(this.portalAnvilContainer);\n\n this.view = render(<Placeholder />, this.root, options);\n\n this.styles.elements.push(\n ...this.attachStyles(this.root, styleUrls),\n ...this.attachStyles(this.portal, styleUrls)\n );\n }\n\n disconnectedCallback() {\n if (!this.root || this.root.isConnected) {\n return;\n }\n for (const element of this.styles.elements) {\n element.remove();\n }\n\n this.view?.unmount();\n document.body?.removeChild(this.portal!.host);\n this.root = undefined;\n this.portal = undefined;\n this.portalAnvilContainer = undefined;\n\n this.onDispose?.();\n }\n\n // Observe changes to the data-mfe-data attribute so that we can re-render when it changes\n static get observedAttributes() {\n return ['data-mfe-data'];\n }\n\n attributeChangedCallback() {\n this.render();\n }\n\n provide = (entries: Entries) => {\n for (const [key, value] of Object.entries(entries)) {\n this[key as keyof IWebComponent] = value;\n }\n\n if (this.styles.loaded === this.styles.total) {\n this.render();\n }\n };\n\n private handleLoad = () => {\n this.styles.loaded++;\n\n if (this.styles.loaded === this.styles.total) {\n this.render();\n }\n };\n\n private attachStyles = (node: ShadowRoot, urls: string[]) => {\n const elements: HTMLElement[] = [];\n\n for (const url of urls) {\n const element = createLink(url, this.handleLoad);\n node.appendChild(element);\n elements.push(element);\n }\n\n return elements;\n };\n\n private withAnvil1Providers(children: ReactElement): ReactElement {\n try {\n /**\n * Note, the webpack.IgnorePlugin must be configured to ignore this dependency.\n * @see {@link file://./../../startup/src/webpack/configs/plugins/ignore-plugin/ignore-plugin.ts}\n */\n const { DefaultPortalContext, TablePopupPropsContext } =\n require('@servicetitan/design-system') as typeof import('@servicetitan/design-system');\n return (\n <DefaultPortalContext.Provider value={this.portal as unknown as HTMLElement}>\n <TablePopupPropsContext.Provider\n value={props => ({\n ...props,\n appendTo: this.root as unknown as HTMLElement,\n })}\n >\n {children}\n </TablePopupPropsContext.Provider>\n </DefaultPortalContext.Provider>\n );\n } catch {\n return children;\n }\n }\n\n private withAnvil2Providers(children: ReactElement): ReactElement {\n try {\n /**\n * Note, the webpack.IgnorePlugin must be configured to ignore this dependency.\n * @see {@link file://./../../startup/src/webpack/configs/plugins/ignore-plugin/ignore-plugin.ts}\n */\n const { PortalProvider } =\n require('@servicetitan/anvil2') as typeof import('@servicetitan/anvil2');\n return <PortalProvider root={this.portalAnvilContainer}>{children}</PortalProvider>;\n } catch {\n return children;\n }\n }\n\n private withProviders(children: ReactElement): ReactElement {\n return this.withAnvil2Providers(this.withAnvil1Providers(children));\n }\n\n private render = () => {\n /*\n * Check for this.root first, otherwise we get error when\n * attributeChangedCallback happens on load\n */\n if (this.root) {\n const mfeData = this.dataset.mfeData\n ? /*\n * Data was stringified in order to come through the data-mfe-data attribute\n * So we must JSON.parse it here to get an object back\n */\n JSON.parse(this.dataset.mfeData)\n : /*\n * If mfeData isn't there, then the Host may not be up to date with web-components\n * and is instead passing data through separate data-attributes, so we want to\n * spread all of those instead\n */\n { ...this.dataset };\n\n const metadata = {\n shadowRoot: this.root,\n portalShadowRoot: this.portal!,\n };\n\n this.view?.rerender(\n <Provider\n singletons={[\n { provide: IS_WEB_COMPONENT_TOKEN, useValue: true },\n { provide: RENDER_ROOT_TOKEN, useValue: this.root },\n ]}\n >\n <Provider\n singletons={[\n { provide: Log, useValue: this.logService },\n {\n provide: HistoryManager,\n useValue: this.historyManager,\n },\n {\n provide: EXPOSED_DEPENDENCIES_TOKEN,\n useValue: this.exposedDependencies,\n },\n {\n provide: EXPOSED_INSTANCE_DEPENDENCIES_TOKEN,\n useValue: this.exposedInstanceDependencies,\n },\n { provide: BASENAME_TOKEN, useValue: this.basename },\n { provide: EVENT_BUS_TOKEN, useValue: this.eventBus },\n { provide: getLDServiceToken(), useValue: this.ldService },\n ].filter(\n ({ provide, useValue }) =>\n useValue !== undefined && provide !== undefined\n )}\n >\n <MFEDataContext.Provider value={{ ...mfeData }}>\n <MFEMetadataContext.Provider value={metadata}>\n {this.withProviders(<Component {...mfeData} />)}\n {this.onReady && <RenderCallback callback={this.onReady} />}\n </MFEMetadataContext.Provider>\n </MFEDataContext.Provider>\n </Provider>\n </Provider>\n );\n }\n };\n }\n\n if (!WEB_COMPONENT_NAME) {\n // eslint-disable-next-line no-console\n console.error('\"WEB_COMPONENT_NAME\" is not defined!');\n return;\n }\n\n window.customElements.define(WEB_COMPONENT_NAME, WebComponent);\n}\n\nfunction getStyleUrls(light: boolean, sharedDependenciesNames: string[], el: HTMLElement) {\n function getCssUrl(dependencyName: string, cssFileRegEx: RegExp) {\n if (light && sharedDependenciesNames.includes(dependencyName)) {\n return Array.from(document.getElementsByTagName('link')).find(\n ({ href }) => href && cssFileRegEx.test(href)\n )?.href;\n }\n }\n\n const designSystemUrl = getCssUrl(\n '@servicetitan/design-system',\n /design-system(\\..+)?\\.bundle\\.css/\n );\n const anvil2CssUrl = getCssUrl('@servicetitan/anvil2', /anvil2(\\..+)?\\.bundle\\.css/);\n\n return [\n ...(designSystemUrl ? [designSystemUrl] : []),\n ...(anvil2CssUrl ? [anvil2CssUrl] : []),\n ...(el.hasAttribute('style-urls')\n ? JSON.parse(decodeURIComponent(el.getAttribute('style-urls')!))\n : []),\n ];\n}\n\nfunction RenderCallback({ callback }: { callback: () => void }) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(() => callback(), []);\n return null;\n}\n"],"names":["Log","Provider","useEffect","BASENAME_TOKEN","EXPOSED_DEPENDENCIES_TOKEN","EXPOSED_INSTANCE_DEPENDENCIES_TOKEN","IS_WEB_COMPONENT_TOKEN","RENDER_ROOT_TOKEN","MFEDataContext","MFEMetadataContext","EVENT_BUS_TOKEN","WEB_COMPONENT_NAME","HistoryManager","render","getElementName","getLDServiceToken","createLink","url","onLoad","element","document","createElement","href","rel","crossOrigin","onload","onerror","Placeholder","div","style","display","register","Component","light","options","WebComponent","HTMLElement","connectedCallback","root","isConnected","sharedDependenciesNames","styleUrls","getStyleUrls","styles","total","length","loaded","elements","shadowRoot","attachShadow","mode","portalElement","setAttribute","portal","body","appendChild","portalAnvilContainer","view","push","attachStyles","disconnectedCallback","remove","unmount","removeChild","host","undefined","onDispose","observedAttributes","attributeChangedCallback","withAnvil1Providers","children","DefaultPortalContext","TablePopupPropsContext","require","value","props","appendTo","withAnvil2Providers","PortalProvider","withProviders","ldService","logService","historyManager","exposedDependencies","exposedInstanceDependencies","eventBus","basename","onReady","provide","entries","key","Object","handleLoad","node","urls","mfeData","dataset","JSON","parse","metadata","portalShadowRoot","rerender","singletons","useValue","filter","RenderCallback","callback","console","error","window","customElements","define","el","getCssUrl","dependencyName","cssFileRegEx","includes","Array","from","getElementsByTagName","find","test","designSystemUrl","anvil2CssUrl","hasAttribute","decodeURIComponent","getAttribute"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/register.tsx"],"sourcesContent":["import { type LDService } from '@servicetitan/launchdarkly-service';\nimport { Log } from '@servicetitan/log-service';\nimport { Provider } from '@servicetitan/react-ioc';\nimport { ExposedDependencies } from '@servicetitan/startup-utils';\nimport { ComponentType, FC, ReactElement, useEffect } from 'react';\nimport { Anvil1Providers } from './anvil1-providers';\nimport { Anvil2Providers } from './anvil2-providers';\nimport {\n BASENAME_TOKEN,\n Entries,\n EXPOSED_DEPENDENCIES_TOKEN,\n EXPOSED_INSTANCE_DEPENDENCIES_TOKEN,\n ExposedInstanceDependencies,\n IS_WEB_COMPONENT_TOKEN,\n IWebComponent,\n RENDER_ROOT_TOKEN,\n} from './common';\nimport { MFEDataContext } from './contexts/mfe-data-context';\nimport { MFEMetadataContext } from './contexts/mfe-metadata-context';\nimport { withCssInjector } from './css-injector';\nimport { EVENT_BUS_TOKEN, EventBus } from './event-bus';\nimport { getStyleUrls } from './get-style-urls';\nimport { WEB_COMPONENT_NAME } from './globals';\nimport { HistoryManager } from './history-manager';\nimport { render } from './render';\nimport { getElementName, getLDServiceToken } from './utils';\n\nfunction createLink(url: string, onLoad: () => void) {\n const element = document.createElement('link');\n\n element.href = url;\n element.rel = 'stylesheet';\n element.crossOrigin = 'anonymous';\n element.onload = onLoad;\n element.onerror = onLoad;\n\n return element;\n}\n\nconst Placeholder: FC = () => <div style={{ display: 'none' }} />;\n\nexport interface RegisterOptions {\n legacyRoot?: boolean;\n sharedDependenciesNames?: string[];\n}\n\nexport function register(Component: ComponentType, light: boolean, options?: RegisterOptions) {\n const WrappedComponent = withCssInjector(Component);\n\n class WebComponent extends HTMLElement implements IWebComponent {\n private root?: ShadowRoot;\n private portal?: ShadowRoot;\n private portalAnvilContainer?: HTMLDivElement;\n\n private styles: {\n total: number;\n loaded: number;\n elements: HTMLElement[];\n } = { total: 0, loaded: 0, elements: [] };\n\n private ldService?: LDService;\n private logService?: Log;\n private historyManager?: HistoryManager;\n private exposedDependencies?: ExposedDependencies;\n private exposedInstanceDependencies?: ExposedInstanceDependencies;\n private eventBus?: EventBus;\n private basename?: string;\n private onReady?: () => void;\n private onDispose?: () => void;\n private view?: ReturnType<typeof render>;\n\n connectedCallback() {\n /*\n * connectedCallback can be called when DOM element has moved\n * check that root is still connected to the DOM\n * if that's the case we don't need to reconnect MFE\n */\n if (this.root?.isConnected) {\n return;\n }\n const sharedDependenciesNames = options?.sharedDependenciesNames ?? [];\n const styleUrls = getStyleUrls(light, sharedDependenciesNames, this);\n\n this.styles = {\n total: styleUrls.length * 2,\n loaded: 0,\n elements: [],\n };\n\n this.root = this.shadowRoot ?? this.attachShadow({ mode: 'open' });\n /*\n * Portal shadow dom is created at the bottom of the body in order to be used as\n * the element for the design system components to attach modals and popups to.\n * This element is passed down the tree through context, and used within\n * the design system components.\n */\n const portalElement = document.createElement('div');\n portalElement.setAttribute(\n 'data-mfe-portal-name',\n getElementName({ WebComponent: WEB_COMPONENT_NAME! })\n );\n this.portal = document.body.appendChild(portalElement).attachShadow({ mode: 'open' });\n /*\n * Add element inside of portal so that Anvil 2 can attach modals and popups to it\n * since floating-ui does not support attaching straight to shadow dom\n */\n this.portalAnvilContainer = document.createElement('div');\n this.portal.appendChild(this.portalAnvilContainer);\n\n this.view = render(<Placeholder />, this.root, options);\n\n this.styles.elements.push(\n ...this.attachStyles(this.root, styleUrls),\n ...this.attachStyles(this.portal, styleUrls)\n );\n }\n\n disconnectedCallback() {\n if (!this.root || this.root.isConnected) {\n return;\n }\n for (const element of this.styles.elements) {\n element.remove();\n }\n\n this.view?.unmount();\n document.body?.removeChild(this.portal!.host);\n this.root = undefined;\n this.portal = undefined;\n this.portalAnvilContainer = undefined;\n\n this.onDispose?.();\n }\n\n // Observe changes to the data-mfe-data attribute so that we can re-render when it changes\n static get observedAttributes() {\n return ['data-mfe-data'];\n }\n\n attributeChangedCallback() {\n this.render();\n }\n\n provide = (entries: Entries) => {\n for (const [key, value] of Object.entries(entries)) {\n this[key as keyof IWebComponent] = value;\n }\n\n if (this.styles.loaded === this.styles.total) {\n this.render();\n }\n };\n\n private handleLoad = () => {\n this.styles.loaded++;\n\n if (this.styles.loaded === this.styles.total) {\n this.render();\n }\n };\n\n private attachStyles = (node: ShadowRoot, urls: string[]) => {\n const elements: HTMLElement[] = [];\n\n for (const url of urls) {\n const element = createLink(url, this.handleLoad);\n node.appendChild(element);\n elements.push(element);\n }\n\n return elements;\n };\n\n private withProviders(children: ReactElement) {\n return (\n <Anvil2Providers portalAnvilContainer={this.portalAnvilContainer!}>\n <Anvil1Providers root={this.root!} portal={this.portal!}>\n {children}\n </Anvil1Providers>\n </Anvil2Providers>\n );\n }\n\n private render = () => {\n /*\n * Check for this.root first, otherwise we get error when\n * attributeChangedCallback happens on load\n */\n if (this.root) {\n const mfeData = this.dataset.mfeData\n ? /*\n * Data was stringified in order to come through the data-mfe-data attribute\n * So we must JSON.parse it here to get an object back\n */\n JSON.parse(this.dataset.mfeData)\n : /*\n * If mfeData isn't there, then the Host may not be up to date with web-components\n * and is instead passing data through separate data-attributes, so we want to\n * spread all of those instead\n */\n { ...this.dataset };\n\n const metadata = {\n shadowRoot: this.root,\n portalShadowRoot: this.portal!,\n };\n\n this.view?.rerender(\n <Provider\n singletons={[\n { provide: IS_WEB_COMPONENT_TOKEN, useValue: true },\n { provide: RENDER_ROOT_TOKEN, useValue: this.root },\n ]}\n >\n <Provider\n singletons={[\n { provide: Log, useValue: this.logService },\n {\n provide: HistoryManager,\n useValue: this.historyManager,\n },\n {\n provide: EXPOSED_DEPENDENCIES_TOKEN,\n useValue: this.exposedDependencies,\n },\n {\n provide: EXPOSED_INSTANCE_DEPENDENCIES_TOKEN,\n useValue: this.exposedInstanceDependencies,\n },\n { provide: BASENAME_TOKEN, useValue: this.basename },\n { provide: EVENT_BUS_TOKEN, useValue: this.eventBus },\n { provide: getLDServiceToken(), useValue: this.ldService },\n ].filter(\n ({ provide, useValue }) =>\n useValue !== undefined && provide !== undefined\n )}\n >\n <MFEDataContext.Provider value={{ ...mfeData }}>\n <MFEMetadataContext.Provider value={metadata}>\n {this.withProviders(<WrappedComponent {...mfeData} />)}\n {this.onReady && <RenderCallback callback={this.onReady} />}\n </MFEMetadataContext.Provider>\n </MFEDataContext.Provider>\n </Provider>\n </Provider>\n );\n }\n };\n }\n\n if (!WEB_COMPONENT_NAME) {\n // eslint-disable-next-line no-console\n console.error('\"WEB_COMPONENT_NAME\" is not defined!');\n return;\n }\n\n window.customElements.define(WEB_COMPONENT_NAME, WebComponent);\n}\n\nfunction RenderCallback({ callback }: { callback: () => void }) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(() => callback(), []);\n return null;\n}\n"],"names":["Log","Provider","useEffect","Anvil1Providers","Anvil2Providers","BASENAME_TOKEN","EXPOSED_DEPENDENCIES_TOKEN","EXPOSED_INSTANCE_DEPENDENCIES_TOKEN","IS_WEB_COMPONENT_TOKEN","RENDER_ROOT_TOKEN","MFEDataContext","MFEMetadataContext","withCssInjector","EVENT_BUS_TOKEN","getStyleUrls","WEB_COMPONENT_NAME","HistoryManager","render","getElementName","getLDServiceToken","createLink","url","onLoad","element","document","createElement","href","rel","crossOrigin","onload","onerror","Placeholder","div","style","display","register","Component","light","options","WrappedComponent","WebComponent","HTMLElement","connectedCallback","root","isConnected","sharedDependenciesNames","styleUrls","styles","total","length","loaded","elements","shadowRoot","attachShadow","mode","portalElement","setAttribute","portal","body","appendChild","portalAnvilContainer","view","push","attachStyles","disconnectedCallback","remove","unmount","removeChild","host","undefined","onDispose","observedAttributes","attributeChangedCallback","withProviders","children","ldService","logService","historyManager","exposedDependencies","exposedInstanceDependencies","eventBus","basename","onReady","provide","entries","key","value","Object","handleLoad","node","urls","mfeData","dataset","JSON","parse","metadata","portalShadowRoot","rerender","singletons","useValue","filter","RenderCallback","callback","console","error","window","customElements","define"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAerC,UAAU,aAAa;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,UAAU,YAAY;IAClB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACtC,OAAO,IAAI,IAAI,CAAC;CACnB;AAED,wBAAgB,MAAM,CAClB,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,OAAO,GAAG,gBAAgB,EACrC,OAAO,CAAC,EAAE,aAAa,GACxB,YAAY,CAgBd"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAIrC,UAAU,aAAa;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,UAAU,YAAY;IAClB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACtC,OAAO,IAAI,IAAI,CAAC;CACnB;AAED,wBAAgB,MAAM,CAClB,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,OAAO,GAAG,gBAAgB,EACrC,OAAO,CAAC,EAAE,aAAa,GACxB,YAAY,CAgBd"}
package/dist/render.js CHANGED
@@ -1,18 +1,10 @@
1
1
  /* eslint-disable react/no-deprecated */ import ReactDOM from 'react-dom';
2
- let createRoot;
3
- try {
4
- /**
5
- * Note, the webpack.IgnorePlugin must be configured to ignore this dependency.
6
- * @see {@link file://./../../startup/src/webpack/configs/plugins/ignore-plugin/ignore-plugin.ts}
7
- */ createRoot = require('react-dom/client').createRoot;
8
- } catch (unused) {
9
- // ignore
10
- }
2
+ import * as ReactDOMClient from 'react-dom/client';
11
3
  export function render(element, container, options) {
12
- if (typeof createRoot !== 'function' || (options === null || options === void 0 ? void 0 : options.legacyRoot)) {
4
+ if (typeof ReactDOMClient.createRoot !== 'function' || (options === null || options === void 0 ? void 0 : options.legacyRoot)) {
13
5
  return renderLegacyRoot(element, container);
14
6
  }
15
- const root = createRoot(container);
7
+ const root = ReactDOMClient.createRoot(container);
16
8
  root.render(element);
17
9
  return {
18
10
  rerender (element) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/render.ts"],"sourcesContent":["/* eslint-disable react/no-deprecated */\nimport { ReactElement } from 'react';\nimport ReactDOM from 'react-dom';\nimport type ReactDOMClient from 'react-dom/client';\n\nlet createRoot: typeof ReactDOMClient.createRoot;\ntry {\n /**\n * Note, the webpack.IgnorePlugin must be configured to ignore this dependency.\n * @see {@link file://./../../startup/src/webpack/configs/plugins/ignore-plugin/ignore-plugin.ts}\n */\n createRoot = require('react-dom/client').createRoot;\n} catch {\n // ignore\n}\n\ninterface RenderOptions {\n legacyRoot?: boolean;\n}\n\ninterface RenderResult {\n rerender(element: ReactElement): void;\n unmount(): void;\n}\n\nexport function render(\n element: ReactElement,\n container: Element | DocumentFragment,\n options?: RenderOptions\n): RenderResult {\n if (typeof createRoot !== 'function' || options?.legacyRoot) {\n return renderLegacyRoot(element, container);\n }\n\n const root = createRoot(container);\n root.render(element);\n\n return {\n rerender(element: ReactElement) {\n root.render(element);\n },\n unmount() {\n root.unmount();\n },\n };\n}\n\nfunction renderLegacyRoot(\n element: ReactElement,\n container: Element | DocumentFragment\n): RenderResult {\n ReactDOM.render(element, container);\n\n return {\n rerender(element: ReactElement) {\n ReactDOM.render(element, container);\n },\n unmount() {\n ReactDOM.unmountComponentAtNode(container);\n },\n };\n}\n"],"names":["ReactDOM","createRoot","require","render","element","container","options","legacyRoot","renderLegacyRoot","root","rerender","unmount","unmountComponentAtNode"],"mappings":"AAAA,sCAAsC,GAEtC,OAAOA,cAAc,YAAY;AAGjC,IAAIC;AACJ,IAAI;IACA;;;KAGC,GACDA,aAAaC,QAAQ,oBAAoBD,UAAU;AACvD,EAAE,eAAM;AACJ,SAAS;AACb;AAWA,OAAO,SAASE,OACZC,OAAqB,EACrBC,SAAqC,EACrCC,OAAuB;IAEvB,IAAI,OAAOL,eAAe,eAAcK,oBAAAA,8BAAAA,QAASC,UAAU,GAAE;QACzD,OAAOC,iBAAiBJ,SAASC;IACrC;IAEA,MAAMI,OAAOR,WAAWI;IACxBI,KAAKN,MAAM,CAACC;IAEZ,OAAO;QACHM,UAASN,OAAqB;YAC1BK,KAAKN,MAAM,CAACC;QAChB;QACAO;YACIF,KAAKE,OAAO;QAChB;IACJ;AACJ;AAEA,SAASH,iBACLJ,OAAqB,EACrBC,SAAqC;IAErCL,SAASG,MAAM,CAACC,SAASC;IAEzB,OAAO;QACHK,UAASN,OAAqB;YAC1BJ,SAASG,MAAM,CAACC,SAASC;QAC7B;QACAM;YACIX,SAASY,sBAAsB,CAACP;QACpC;IACJ;AACJ"}
1
+ {"version":3,"sources":["../src/render.ts"],"sourcesContent":["/* eslint-disable react/no-deprecated */\nimport { ReactElement } from 'react';\nimport ReactDOM from 'react-dom';\nimport * as ReactDOMClient from 'react-dom/client';\n\ninterface RenderOptions {\n legacyRoot?: boolean;\n}\n\ninterface RenderResult {\n rerender(element: ReactElement): void;\n unmount(): void;\n}\n\nexport function render(\n element: ReactElement,\n container: Element | DocumentFragment,\n options?: RenderOptions\n): RenderResult {\n if (typeof ReactDOMClient.createRoot !== 'function' || options?.legacyRoot) {\n return renderLegacyRoot(element, container);\n }\n\n const root = ReactDOMClient.createRoot(container);\n root.render(element);\n\n return {\n rerender(element: ReactElement) {\n root.render(element);\n },\n unmount() {\n root.unmount();\n },\n };\n}\n\nfunction renderLegacyRoot(\n element: ReactElement,\n container: Element | DocumentFragment\n): RenderResult {\n ReactDOM.render(element, container);\n\n return {\n rerender(element: ReactElement) {\n ReactDOM.render(element, container);\n },\n unmount() {\n ReactDOM.unmountComponentAtNode(container);\n },\n };\n}\n"],"names":["ReactDOM","ReactDOMClient","render","element","container","options","createRoot","legacyRoot","renderLegacyRoot","root","rerender","unmount","unmountComponentAtNode"],"mappings":"AAAA,sCAAsC,GAEtC,OAAOA,cAAc,YAAY;AACjC,YAAYC,oBAAoB,mBAAmB;AAWnD,OAAO,SAASC,OACZC,OAAqB,EACrBC,SAAqC,EACrCC,OAAuB;IAEvB,IAAI,OAAOJ,eAAeK,UAAU,KAAK,eAAcD,oBAAAA,8BAAAA,QAASE,UAAU,GAAE;QACxE,OAAOC,iBAAiBL,SAASC;IACrC;IAEA,MAAMK,OAAOR,eAAeK,UAAU,CAACF;IACvCK,KAAKP,MAAM,CAACC;IAEZ,OAAO;QACHO,UAASP,OAAqB;YAC1BM,KAAKP,MAAM,CAACC;QAChB;QACAQ;YACIF,KAAKE,OAAO;QAChB;IACJ;AACJ;AAEA,SAASH,iBACLL,OAAqB,EACrBC,SAAqC;IAErCJ,SAASE,MAAM,CAACC,SAASC;IAEzB,OAAO;QACHM,UAASP,OAAqB;YAC1BH,SAASE,MAAM,CAACC,SAASC;QAC7B;QACAO;YACIX,SAASY,sBAAsB,CAACR;QACpC;IACJ;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-bundle-info.d.ts","sourceRoot":"","sources":["../../src/utils/get-bundle-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAwB,MAAM,6BAA6B,CAAC;AAGxF,OAAO,EAAE,2BAA2B,EAAW,YAAY,EAAE,MAAM,WAAW,CAAC;AAI/E,OAAO,EAAE,WAAW,EAAkB,MAAM,oBAAoB,CAAC;AAKjE,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC;AAEnE,UAAU,kBAAkB;IACxB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,wBAAsB,aAAa,CAAC,EAChC,KAAK,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAwB,EACxB,2BAAgC,EAChC,UAAU,EACV,MAAM,EACN,OAAO,EACP,cAAc,GACjB,EAAE,kBAAkB;;;;;;;;;;;;;;;;GAuFpB"}
1
+ {"version":3,"file":"get-bundle-info.d.ts","sourceRoot":"","sources":["../../src/utils/get-bundle-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAwB,MAAM,6BAA6B,CAAC;AAGxF,OAAO,EAAE,2BAA2B,EAAW,YAAY,EAAE,MAAM,WAAW,CAAC;AAI/E,OAAO,EAAE,WAAW,EAAkB,MAAM,oBAAoB,CAAC;AAKjE,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC;AAEnE,UAAU,kBAAkB;IACxB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,wBAAsB,aAAa,CAAC,EAChC,KAAK,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAwB,EACxB,2BAAgC,EAChC,UAAU,EACV,MAAM,EACN,OAAO,EACP,cAAc,GACjB,EAAE,kBAAkB;;;;;;;;;;;;;;;;GA8FpB"}
@@ -43,12 +43,12 @@ export async function getBundleInfo({ cache, mainPackageUrl, fallbackPackageUrl,
43
43
  const embed = !mismatch && supportLightBundles;
44
44
  resolvedBundleType = embed ? 'light' : 'full';
45
45
  }
46
- const withBaseUrl = (path)=>`${exactPackageUrl}/dist/bundle/${resolvedBundleType}/${path}`;
46
+ const bundleBaseUrl = `${exactPackageUrl}/dist/bundle/${resolvedBundleType}`;
47
47
  let entrypoints;
48
48
  if ((_metadata_entrypoints = metadata.entrypoints) === null || _metadata_entrypoints === void 0 ? void 0 : _metadata_entrypoints[resolvedBundleType]) {
49
49
  entrypoints = metadata.entrypoints[resolvedBundleType];
50
50
  } else {
51
- entrypoints = await getEntrypoints(withBaseUrl('entrypoints.json'), requestCache);
51
+ entrypoints = await getEntrypoints(`${bundleBaseUrl}/entrypoints.json`, requestCache);
52
52
  }
53
53
  const disposer = ()=>{
54
54
  if (resolvedBundleType === 'light') {
@@ -58,9 +58,16 @@ export async function getBundleInfo({ cache, mainPackageUrl, fallbackPackageUrl,
58
58
  }
59
59
  }
60
60
  };
61
+ let baseUrl = bundleBaseUrl;
62
+ // Per-MFE dev-server port only happens during local development
63
+ if (process.env.NODE_ENV !== 'production' && entrypoints.port) {
64
+ const url = new URL(baseUrl);
65
+ url.port = String(entrypoints.port);
66
+ baseUrl = url.toString();
67
+ }
61
68
  const urls = {
62
- css: entrypoints.css.map(withBaseUrl),
63
- js: entrypoints.js.map(withBaseUrl)
69
+ css: entrypoints.css.map((path)=>`${baseUrl}/${path}`),
70
+ js: entrypoints.js.map((path)=>`${baseUrl}/${path}`)
64
71
  };
65
72
  const elementName = resolvedBundleType === 'headless' ? `headless-${name}` : name;
66
73
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/get-bundle-info.ts"],"sourcesContent":["import { ExposedDependencies, getVersionMismatches } from '@servicetitan/startup-utils';\nimport get from 'lodash.get';\nimport semver from 'semver';\nimport { ExposedInstanceDependencies, getJson, GetJsonError } from '../common';\nimport type { Metadata } from '../types';\nimport { getCacheSetting } from './get-cache-setting';\nimport { getCachedMetadata } from './get-cached-metadata';\nimport { EntryPoints, getEntrypoints } from './get-entry-points';\nimport { getMFEHasCompatibleLaunchDarklyVersion } from './get-mfe-has-compatible-launchdarkly-version';\nimport { getMFEHasUniqueChunkNamespace } from './get-mfe-has-unique-chunk-namespace';\nimport { getMFESupportsRerendering } from './get-mfe-supports-rerendering';\n\nexport type BundleInfo = Awaited<ReturnType<typeof getBundleInfo>>;\n\ninterface GetBundleInfoProps {\n cache: boolean | number;\n exposedDependencies?: ExposedDependencies;\n exposedInstanceDependencies?: ExposedInstanceDependencies;\n fallbackPackageUrl?: string;\n isHeadless?: boolean;\n mainPackageUrl: string;\n onRequestError?: (error: GetJsonError) => void;\n retries?: number;\n signal?: AbortSignal;\n}\n\nexport async function getBundleInfo({\n cache,\n mainPackageUrl,\n fallbackPackageUrl,\n exposedDependencies = {},\n exposedInstanceDependencies = {},\n isHeadless,\n signal,\n retries,\n onRequestError,\n}: GetBundleInfoProps) {\n const {\n cacheHit,\n data: metadata,\n url: packageUrl,\n } = await getCachedMetadata({\n cache,\n fallbackPackageUrl,\n mainPackageUrl,\n onRequestError,\n retries,\n signal,\n });\n\n const { bundledWith, name, sharedDependencies } = metadata;\n const exactPackageUrl = packageUrl.replace('/dist/metadata.json', '');\n const requestCache = getCacheSetting(exactPackageUrl);\n\n let dependencies: NonNullable<Metadata['dependencies']>;\n if (metadata.dependencies) {\n dependencies = metadata.dependencies;\n } else {\n const { data: packageJson } = await getJson(`${exactPackageUrl}/package.json`, {\n cache: requestCache,\n retries,\n signal,\n });\n\n dependencies = packageJson.dependencies;\n }\n\n let mismatch: ReturnType<typeof getVersionMismatches>;\n let resolvedBundleType: 'headless' | 'light' | 'full';\n\n if (isHeadless) {\n resolvedBundleType = 'headless';\n } else {\n mismatch = getVersionMismatches(exposedDependencies, dependencies, sharedDependencies);\n const startupVersion = bundledWith?.['@servicetitan/startup'];\n const supportLightBundles = !!startupVersion && semver.gte(startupVersion, '17.0.0');\n const embed = !mismatch && supportLightBundles;\n resolvedBundleType = embed ? 'light' : 'full';\n }\n\n const withBaseUrl = (path: string) =>\n `${exactPackageUrl}/dist/bundle/${resolvedBundleType}/${path}`;\n\n let entrypoints: EntryPoints;\n if (metadata.entrypoints?.[resolvedBundleType]) {\n entrypoints = metadata.entrypoints[resolvedBundleType];\n } else {\n entrypoints = await getEntrypoints(withBaseUrl('entrypoints.json'), requestCache);\n }\n\n const disposer = () => {\n if (resolvedBundleType === 'light') {\n const path = Object.values<string>(sharedDependencies).shift();\n if (path) {\n get(window, path).getStorage().clear(name);\n }\n }\n };\n\n const urls: EntryPoints = {\n css: entrypoints.css.map(withBaseUrl),\n js: entrypoints.js.map(withBaseUrl),\n };\n\n const elementName = resolvedBundleType === 'headless' ? `headless-${name}` : name;\n\n return {\n bundledWith,\n cacheHit,\n disposer,\n mfeSupportsRerendering: getMFESupportsRerendering(bundledWith, dependencies),\n mfeHasUniqueChunkNamespace: getMFEHasUniqueChunkNamespace(bundledWith),\n mfeHasCompatibleLaunchDarklyVersion: getMFEHasCompatibleLaunchDarklyVersion(\n bundledWith,\n exposedInstanceDependencies\n ),\n mismatch,\n resolvedBundleType,\n urls,\n WebComponent: elementName,\n version: metadata.version,\n module: entrypoints.module,\n };\n}\n"],"names":["getVersionMismatches","get","semver","getJson","getCacheSetting","getCachedMetadata","getEntrypoints","getMFEHasCompatibleLaunchDarklyVersion","getMFEHasUniqueChunkNamespace","getMFESupportsRerendering","getBundleInfo","cache","mainPackageUrl","fallbackPackageUrl","exposedDependencies","exposedInstanceDependencies","isHeadless","signal","retries","onRequestError","metadata","cacheHit","data","url","packageUrl","bundledWith","name","sharedDependencies","exactPackageUrl","replace","requestCache","dependencies","packageJson","mismatch","resolvedBundleType","startupVersion","supportLightBundles","gte","embed","withBaseUrl","path","entrypoints","disposer","Object","values","shift","window","getStorage","clear","urls","css","map","js","elementName","mfeSupportsRerendering","mfeHasUniqueChunkNamespace","mfeHasCompatibleLaunchDarklyVersion","WebComponent","version","module"],"mappings":"AAAA,SAA8BA,oBAAoB,QAAQ,8BAA8B;AACxF,OAAOC,SAAS,aAAa;AAC7B,OAAOC,YAAY,SAAS;AAC5B,SAAsCC,OAAO,QAAsB,YAAY;AAE/E,SAASC,eAAe,QAAQ,sBAAsB;AACtD,SAASC,iBAAiB,QAAQ,wBAAwB;AAC1D,SAAsBC,cAAc,QAAQ,qBAAqB;AACjE,SAASC,sCAAsC,QAAQ,gDAAgD;AACvG,SAASC,6BAA6B,QAAQ,uCAAuC;AACrF,SAASC,yBAAyB,QAAQ,iCAAiC;AAgB3E,OAAO,eAAeC,cAAc,EAChCC,KAAK,EACLC,cAAc,EACdC,kBAAkB,EAClBC,sBAAsB,CAAC,CAAC,EACxBC,8BAA8B,CAAC,CAAC,EAChCC,UAAU,EACVC,MAAM,EACNC,OAAO,EACPC,cAAc,EACG;QAgDbC;IA/CJ,MAAM,EACFC,QAAQ,EACRC,MAAMF,QAAQ,EACdG,KAAKC,UAAU,EAClB,GAAG,MAAMnB,kBAAkB;QACxBM;QACAE;QACAD;QACAO;QACAD;QACAD;IACJ;IAEA,MAAM,EAAEQ,WAAW,EAAEC,IAAI,EAAEC,kBAAkB,EAAE,GAAGP;IAClD,MAAMQ,kBAAkBJ,WAAWK,OAAO,CAAC,uBAAuB;IAClE,MAAMC,eAAe1B,gBAAgBwB;IAErC,IAAIG;IACJ,IAAIX,SAASW,YAAY,EAAE;QACvBA,eAAeX,SAASW,YAAY;IACxC,OAAO;QACH,MAAM,EAAET,MAAMU,WAAW,EAAE,GAAG,MAAM7B,QAAQ,GAAGyB,gBAAgB,aAAa,CAAC,EAAE;YAC3EjB,OAAOmB;YACPZ;YACAD;QACJ;QAEAc,eAAeC,YAAYD,YAAY;IAC3C;IAEA,IAAIE;IACJ,IAAIC;IAEJ,IAAIlB,YAAY;QACZkB,qBAAqB;IACzB,OAAO;QACHD,WAAWjC,qBAAqBc,qBAAqBiB,cAAcJ;QACnE,MAAMQ,iBAAiBV,wBAAAA,kCAAAA,WAAa,CAAC,wBAAwB;QAC7D,MAAMW,sBAAsB,CAAC,CAACD,kBAAkBjC,OAAOmC,GAAG,CAACF,gBAAgB;QAC3E,MAAMG,QAAQ,CAACL,YAAYG;QAC3BF,qBAAqBI,QAAQ,UAAU;IAC3C;IAEA,MAAMC,cAAc,CAACC,OACjB,GAAGZ,gBAAgB,aAAa,EAAEM,mBAAmB,CAAC,EAAEM,MAAM;IAElE,IAAIC;IACJ,KAAIrB,wBAAAA,SAASqB,WAAW,cAApBrB,4CAAAA,qBAAsB,CAACc,mBAAmB,EAAE;QAC5CO,cAAcrB,SAASqB,WAAW,CAACP,mBAAmB;IAC1D,OAAO;QACHO,cAAc,MAAMnC,eAAeiC,YAAY,qBAAqBT;IACxE;IAEA,MAAMY,WAAW;QACb,IAAIR,uBAAuB,SAAS;YAChC,MAAMM,OAAOG,OAAOC,MAAM,CAASjB,oBAAoBkB,KAAK;YAC5D,IAAIL,MAAM;gBACNvC,IAAI6C,QAAQN,MAAMO,UAAU,GAAGC,KAAK,CAACtB;YACzC;QACJ;IACJ;IAEA,MAAMuB,OAAoB;QACtBC,KAAKT,YAAYS,GAAG,CAACC,GAAG,CAACZ;QACzBa,IAAIX,YAAYW,EAAE,CAACD,GAAG,CAACZ;IAC3B;IAEA,MAAMc,cAAcnB,uBAAuB,aAAa,CAAC,SAAS,EAAER,MAAM,GAAGA;IAE7E,OAAO;QACHD;QACAJ;QACAqB;QACAY,wBAAwB7C,0BAA0BgB,aAAaM;QAC/DwB,4BAA4B/C,8BAA8BiB;QAC1D+B,qCAAqCjD,uCACjCkB,aACAV;QAEJkB;QACAC;QACAe;QACAQ,cAAcJ;QACdK,SAAStC,SAASsC,OAAO;QACzBC,QAAQlB,YAAYkB,MAAM;IAC9B;AACJ"}
1
+ {"version":3,"sources":["../../src/utils/get-bundle-info.ts"],"sourcesContent":["import { ExposedDependencies, getVersionMismatches } from '@servicetitan/startup-utils';\nimport get from 'lodash.get';\nimport semver from 'semver';\nimport { ExposedInstanceDependencies, getJson, GetJsonError } from '../common';\nimport type { Metadata } from '../types';\nimport { getCacheSetting } from './get-cache-setting';\nimport { getCachedMetadata } from './get-cached-metadata';\nimport { EntryPoints, getEntrypoints } from './get-entry-points';\nimport { getMFEHasCompatibleLaunchDarklyVersion } from './get-mfe-has-compatible-launchdarkly-version';\nimport { getMFEHasUniqueChunkNamespace } from './get-mfe-has-unique-chunk-namespace';\nimport { getMFESupportsRerendering } from './get-mfe-supports-rerendering';\n\nexport type BundleInfo = Awaited<ReturnType<typeof getBundleInfo>>;\n\ninterface GetBundleInfoProps {\n cache: boolean | number;\n exposedDependencies?: ExposedDependencies;\n exposedInstanceDependencies?: ExposedInstanceDependencies;\n fallbackPackageUrl?: string;\n isHeadless?: boolean;\n mainPackageUrl: string;\n onRequestError?: (error: GetJsonError) => void;\n retries?: number;\n signal?: AbortSignal;\n}\n\nexport async function getBundleInfo({\n cache,\n mainPackageUrl,\n fallbackPackageUrl,\n exposedDependencies = {},\n exposedInstanceDependencies = {},\n isHeadless,\n signal,\n retries,\n onRequestError,\n}: GetBundleInfoProps) {\n const {\n cacheHit,\n data: metadata,\n url: packageUrl,\n } = await getCachedMetadata({\n cache,\n fallbackPackageUrl,\n mainPackageUrl,\n onRequestError,\n retries,\n signal,\n });\n\n const { bundledWith, name, sharedDependencies } = metadata;\n const exactPackageUrl = packageUrl.replace('/dist/metadata.json', '');\n const requestCache = getCacheSetting(exactPackageUrl);\n\n let dependencies: NonNullable<Metadata['dependencies']>;\n if (metadata.dependencies) {\n dependencies = metadata.dependencies;\n } else {\n const { data: packageJson } = await getJson(`${exactPackageUrl}/package.json`, {\n cache: requestCache,\n retries,\n signal,\n });\n\n dependencies = packageJson.dependencies;\n }\n\n let mismatch: ReturnType<typeof getVersionMismatches>;\n let resolvedBundleType: 'headless' | 'light' | 'full';\n\n if (isHeadless) {\n resolvedBundleType = 'headless';\n } else {\n mismatch = getVersionMismatches(exposedDependencies, dependencies, sharedDependencies);\n const startupVersion = bundledWith?.['@servicetitan/startup'];\n const supportLightBundles = !!startupVersion && semver.gte(startupVersion, '17.0.0');\n const embed = !mismatch && supportLightBundles;\n resolvedBundleType = embed ? 'light' : 'full';\n }\n\n const bundleBaseUrl = `${exactPackageUrl}/dist/bundle/${resolvedBundleType}`;\n\n let entrypoints: EntryPoints;\n if (metadata.entrypoints?.[resolvedBundleType]) {\n entrypoints = metadata.entrypoints[resolvedBundleType];\n } else {\n entrypoints = await getEntrypoints(`${bundleBaseUrl}/entrypoints.json`, requestCache);\n }\n\n const disposer = () => {\n if (resolvedBundleType === 'light') {\n const path = Object.values<string>(sharedDependencies).shift();\n if (path) {\n get(window, path).getStorage().clear(name);\n }\n }\n };\n\n let baseUrl = bundleBaseUrl;\n // Per-MFE dev-server port only happens during local development\n if (process.env.NODE_ENV !== 'production' && entrypoints.port) {\n const url = new URL(baseUrl);\n url.port = String(entrypoints.port);\n baseUrl = url.toString();\n }\n\n const urls: EntryPoints = {\n css: entrypoints.css.map(path => `${baseUrl}/${path}`),\n js: entrypoints.js.map(path => `${baseUrl}/${path}`),\n };\n\n const elementName = resolvedBundleType === 'headless' ? `headless-${name}` : name;\n\n return {\n bundledWith,\n cacheHit,\n disposer,\n mfeSupportsRerendering: getMFESupportsRerendering(bundledWith, dependencies),\n mfeHasUniqueChunkNamespace: getMFEHasUniqueChunkNamespace(bundledWith),\n mfeHasCompatibleLaunchDarklyVersion: getMFEHasCompatibleLaunchDarklyVersion(\n bundledWith,\n exposedInstanceDependencies\n ),\n mismatch,\n resolvedBundleType,\n urls,\n WebComponent: elementName,\n version: metadata.version,\n module: entrypoints.module,\n };\n}\n"],"names":["getVersionMismatches","get","semver","getJson","getCacheSetting","getCachedMetadata","getEntrypoints","getMFEHasCompatibleLaunchDarklyVersion","getMFEHasUniqueChunkNamespace","getMFESupportsRerendering","getBundleInfo","cache","mainPackageUrl","fallbackPackageUrl","exposedDependencies","exposedInstanceDependencies","isHeadless","signal","retries","onRequestError","metadata","cacheHit","data","url","packageUrl","bundledWith","name","sharedDependencies","exactPackageUrl","replace","requestCache","dependencies","packageJson","mismatch","resolvedBundleType","startupVersion","supportLightBundles","gte","embed","bundleBaseUrl","entrypoints","disposer","path","Object","values","shift","window","getStorage","clear","baseUrl","process","env","NODE_ENV","port","URL","String","toString","urls","css","map","js","elementName","mfeSupportsRerendering","mfeHasUniqueChunkNamespace","mfeHasCompatibleLaunchDarklyVersion","WebComponent","version","module"],"mappings":"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"}
@@ -2,6 +2,7 @@ export interface EntryPoints {
2
2
  css: string[];
3
3
  js: string[];
4
4
  module?: true;
5
+ port?: number;
5
6
  }
6
7
  export declare function getEntrypoints(url: string, cache: 'no-store' | undefined, signal?: AbortSignal): Promise<EntryPoints>;
7
8
  //# sourceMappingURL=get-entry-points.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-entry-points.d.ts","sourceRoot":"","sources":["../../src/utils/get-entry-points.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IACxB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,MAAM,CAAC,EAAE,IAAI,CAAC;CACjB;AAED,wBAAsB,cAAc,CAChC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,WAAW,CAAC,CAWtB"}
1
+ {"version":3,"file":"get-entry-points.d.ts","sourceRoot":"","sources":["../../src/utils/get-entry-points.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IACxB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,cAAc,CAChC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,WAAW,CAAC,CAWtB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/get-entry-points.ts"],"sourcesContent":["import { getJson } from '../common';\n\nexport interface EntryPoints {\n css: string[];\n js: string[];\n module?: true;\n}\n\nexport async function getEntrypoints(\n url: string,\n cache: 'no-store' | undefined,\n signal?: AbortSignal\n): Promise<EntryPoints> {\n try {\n return (\n await getJson(url, {\n cache,\n signal,\n })\n ).data;\n } catch {\n return { css: ['index.css'], js: ['index.js'] };\n }\n}\n"],"names":["getJson","getEntrypoints","url","cache","signal","data","css","js"],"mappings":"AAAA,SAASA,OAAO,QAAQ,YAAY;AAQpC,OAAO,eAAeC,eAClBC,GAAW,EACXC,KAA6B,EAC7BC,MAAoB;IAEpB,IAAI;QACA,OAAO,AACH,CAAA,MAAMJ,QAAQE,KAAK;YACfC;YACAC;QACJ,EAAC,EACHC,IAAI;IACV,EAAE,eAAM;QACJ,OAAO;YAAEC,KAAK;gBAAC;aAAY;YAAEC,IAAI;gBAAC;aAAW;QAAC;IAClD;AACJ"}
1
+ {"version":3,"sources":["../../src/utils/get-entry-points.ts"],"sourcesContent":["import { getJson } from '../common';\n\nexport interface EntryPoints {\n css: string[];\n js: string[];\n module?: true;\n port?: number;\n}\n\nexport async function getEntrypoints(\n url: string,\n cache: 'no-store' | undefined,\n signal?: AbortSignal\n): Promise<EntryPoints> {\n try {\n return (\n await getJson(url, {\n cache,\n signal,\n })\n ).data;\n } catch {\n return { css: ['index.css'], js: ['index.js'] };\n }\n}\n"],"names":["getJson","getEntrypoints","url","cache","signal","data","css","js"],"mappings":"AAAA,SAASA,OAAO,QAAQ,YAAY;AASpC,OAAO,eAAeC,eAClBC,GAAW,EACXC,KAA6B,EAC7BC,MAAoB;IAEpB,IAAI;QACA,OAAO,AACH,CAAA,MAAMJ,QAAQE,KAAK;YACfC;YACAC;QACJ,EAAC,EACHC,IAAI;IACV,EAAE,eAAM;QACJ,OAAO;YAAEC,KAAK;gBAAC;aAAY;YAAEC,IAAI;gBAAC;aAAW;QAAC;IAClD;AACJ"}
@@ -1,4 +1,3 @@
1
- import { type LDService } from '@servicetitan/launchdarkly-service';
2
- import { SymbolToken } from '@servicetitan/react-ioc';
3
- export declare function getLDServiceToken(): SymbolToken<LDService>;
1
+ import * as LDService from '@servicetitan/launchdarkly-service';
2
+ export declare function getLDServiceToken(): import("@servicetitan/react-ioc").SymbolToken<LDService.LDService>;
4
3
  //# sourceMappingURL=get-ld-service-token.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-ld-service-token.d.ts","sourceRoot":"","sources":["../../src/utils/get-ld-service-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAStD,wBAAgB,iBAAiB,2BAEhC"}
1
+ {"version":3,"file":"get-ld-service-token.d.ts","sourceRoot":"","sources":["../../src/utils/get-ld-service-token.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oCAAoC,CAAC;AAEhE,wBAAgB,iBAAiB,uEAEhC"}
@@ -1,11 +1,6 @@
1
- let token;
2
- try {
3
- token = require('@servicetitan/launchdarkly-service').LD_SERVICE_TOKEN;
4
- } catch (unused) {
5
- // ignore
6
- }
1
+ import * as LDService from '@servicetitan/launchdarkly-service';
7
2
  export function getLDServiceToken() {
8
- return token;
3
+ return LDService.LD_SERVICE_TOKEN;
9
4
  }
10
5
 
11
6
  //# sourceMappingURL=get-ld-service-token.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/get-ld-service-token.ts"],"sourcesContent":["import { type LDService } from '@servicetitan/launchdarkly-service';\nimport { SymbolToken } from '@servicetitan/react-ioc';\n\nlet token: SymbolToken<LDService>;\ntry {\n token = require('@servicetitan/launchdarkly-service').LD_SERVICE_TOKEN;\n} catch {\n // ignore\n}\n\nexport function getLDServiceToken() {\n return token;\n}\n"],"names":["token","require","LD_SERVICE_TOKEN","getLDServiceToken"],"mappings":"AAGA,IAAIA;AACJ,IAAI;IACAA,QAAQC,QAAQ,sCAAsCC,gBAAgB;AAC1E,EAAE,eAAM;AACJ,SAAS;AACb;AAEA,OAAO,SAASC;IACZ,OAAOH;AACX"}
1
+ {"version":3,"sources":["../../src/utils/get-ld-service-token.ts"],"sourcesContent":["import * as LDService from '@servicetitan/launchdarkly-service';\n\nexport function getLDServiceToken() {\n return LDService.LD_SERVICE_TOKEN;\n}\n"],"names":["LDService","getLDServiceToken","LD_SERVICE_TOKEN"],"mappings":"AAAA,YAAYA,eAAe,qCAAqC;AAEhE,OAAO,SAASC;IACZ,OAAOD,UAAUE,gBAAgB;AACrC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/web-components",
3
- "version": "36.3.1",
3
+ "version": "37.0.0",
4
4
  "description": "Framework to build, register, and load Microfrontends into host applications",
5
5
  "homepage": "https://docs.st.dev/docs/frontend/uikit/web-components",
6
6
  "repository": {
@@ -10,24 +10,35 @@
10
10
  },
11
11
  "main": "./dist/index.js",
12
12
  "typings": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./register-headless": {
19
+ "types": "./dist/register-headless.d.ts",
20
+ "default": "./dist/register-headless.js"
21
+ },
22
+ "./*": "./dist/*.js"
23
+ },
13
24
  "sideEffects": false,
14
25
  "files": [
15
26
  "dist",
16
27
  "src"
17
28
  ],
18
29
  "dependencies": {
19
- "@servicetitan/error-boundary": "36.3.1",
20
- "@servicetitan/startup-utils": "36.3.1",
30
+ "@servicetitan/error-boundary": "37.0.0",
31
+ "@servicetitan/startup-utils": "37.0.0",
21
32
  "lodash.get": "~4.4.2",
22
33
  "mitt": "~3.0.1",
23
- "semver": "~7.8.0"
34
+ "semver": "~7.8.4"
24
35
  },
25
36
  "devDependencies": {
26
- "@servicetitan/anvil2": "^3.0.6",
37
+ "@servicetitan/anvil2": "^3.4.0",
27
38
  "@servicetitan/design-system": "~14.5.3",
28
- "@servicetitan/launchdarkly-service": "36.3.1",
29
- "@servicetitan/log-service": "36.3.1",
30
- "@servicetitan/react-ioc": "36.3.1",
39
+ "@servicetitan/launchdarkly-service": "37.0.0",
40
+ "@servicetitan/log-service": "37.0.0",
41
+ "@servicetitan/react-ioc": "37.0.0",
31
42
  "@testing-library/dom": "^10.4.1",
32
43
  "@testing-library/jest-dom": "^6.9.1",
33
44
  "@testing-library/react": "^16.3.2",
@@ -40,14 +51,14 @@
40
51
  "launchdarkly-react-client-sdk": "3.6.1",
41
52
  "react": "~18.3.1",
42
53
  "react-dom": "~18.3.1",
43
- "shadow-dom-testing-library": "^1.13.1"
54
+ "shadow-dom-testing-library": "^1.14.0"
44
55
  },
45
56
  "peerDependencies": {
46
57
  "@servicetitan/anvil2": ">=1.5.0",
47
58
  "@servicetitan/design-system": ">=14.5.3",
48
- "@servicetitan/launchdarkly-service": "36.3.1",
49
- "@servicetitan/log-service": "36.3.1",
50
- "@servicetitan/react-ioc": "36.3.1",
59
+ "@servicetitan/launchdarkly-service": "37.0.0",
60
+ "@servicetitan/log-service": "37.0.0",
61
+ "@servicetitan/react-ioc": "37.0.0",
51
62
  "history": "~4.10.1",
52
63
  "react": ">=17.0.2",
53
64
  "react-dom": ">=17.0.2"
@@ -67,7 +78,18 @@
67
78
  "access": "public"
68
79
  },
69
80
  "cli": {
70
- "webpack": false
81
+ "webpack": false,
82
+ "peerDependenciesMeta": {
83
+ "@servicetitan/anvil2": {
84
+ "fallback": "empty"
85
+ },
86
+ "@servicetitan/design-system": {
87
+ "fallback": "empty"
88
+ },
89
+ "@servicetitan/launchdarkly-service": {
90
+ "fallback": "empty"
91
+ }
92
+ }
71
93
  },
72
- "gitHead": "93a049ba1e5cb737afb7bc95c06020736bc61a38"
94
+ "gitHead": "f0acf332ece207c91acb18da23a614efbc8551ec"
73
95
  }
@@ -0,0 +1,49 @@
1
+ import { render } from '@testing-library/react';
2
+ import { MockComponent, registerComponent } from './index';
3
+
4
+ /* Wrapper components injected by each optional library. */
5
+ export const optionalLibraryWrappers: Record<string, string[]> = {
6
+ '@servicetitan/anvil2': ['PortalProvider'],
7
+ '@servicetitan/design-system': [
8
+ 'DefaultPortalContext.Provider',
9
+ 'TablePopupPropsContext.Provider',
10
+ ],
11
+ };
12
+
13
+ export interface RegisterHarness {
14
+ Page: {
15
+ readonly componentShadowRoot: ShadowRoot;
16
+ readonly portalShadowRoot: ShadowRoot;
17
+ };
18
+ subject: () => ReturnType<typeof render>;
19
+ }
20
+
21
+ export function createRegisterHarness(
22
+ componentName: string,
23
+ options?: Parameters<typeof registerComponent>[2]
24
+ ): RegisterHarness {
25
+ const Page = {
26
+ get componentShadowRoot() {
27
+ return document.querySelector(componentName)!.shadowRoot!;
28
+ },
29
+ get portalShadowRoot() {
30
+ return document.querySelector('body')!.lastElementChild!.shadowRoot!;
31
+ },
32
+ };
33
+
34
+ beforeAll(() => {
35
+ registerComponent(MockComponent, componentName, {
36
+ sharedDependenciesNames: ['@servicetitan/anvil2'],
37
+ ...options,
38
+ });
39
+ });
40
+
41
+ const subject = () => {
42
+ jest.spyOn(console, 'error').mockImplementation(jest.fn()); // suppress "ReactDOM.render deprecated" error
43
+ const Component: any = componentName;
44
+ const handleRef = (wc: any) => wc?.provide({ foo: 'bar' }); // triggers rendering
45
+ return render(<Component ref={handleRef} />);
46
+ };
47
+
48
+ return { Page, subject };
49
+ }
@@ -1,13 +1,20 @@
1
1
  import { TablePopupPropsContext } from '@servicetitan/design-system';
2
- import { FC, useContext } from 'react';
2
+ import { createContext, FC, useContext } from 'react';
3
3
  import { useMFEDataContext, useMFEMetadataContext } from '../contexts';
4
4
 
5
5
  jest.mock('@progress/kendo-licensing', () => ({ validatePackage: jest.fn() }));
6
6
 
7
+ const FallbackContext = createContext(undefined);
8
+
7
9
  export const MockComponent: FC<Record<string, any>> = props => {
8
10
  const data = useMFEDataContext();
9
11
  const metadata = useMFEMetadataContext();
10
- const popupProps = useContext(TablePopupPropsContext);
12
+ /*
13
+ * TablePopupPropsContext is undefined when a test mocks
14
+ * @servicetitan/design-system as an empty object. Fall back to
15
+ * a no-op context so the component renders without it.
16
+ */
17
+ const popupProps = useContext(TablePopupPropsContext ?? FallbackContext);
11
18
  const appendTo = popupProps?.({}).appendTo;
12
19
 
13
20
  return (
@@ -403,6 +403,38 @@ describe('[web-components] Loader', () => {
403
403
  () => `${baseUrl}/dist/bundle/light/${metadata.entrypoints!.light.css[0]}`
404
404
  );
405
405
 
406
+ describe('when the entrypoint specifies a dev-server port', () => {
407
+ const port = 3000;
408
+
409
+ beforeEach(() => {
410
+ Object.assign(metadata.entrypoints!.light, { port });
411
+ });
412
+
413
+ function withPort(url: string) {
414
+ const result = new URL(url);
415
+ result.port = String(port);
416
+ return String(result);
417
+ }
418
+
419
+ itAddsJsEntrypointToWebComponent(
420
+ 'entrypoint with dev-server port',
421
+ () => `${withPort(baseUrl)}/dist/bundle/light/${metadata.entrypoints!.light.js[0]}`
422
+ );
423
+
424
+ describe('when NODE_ENV is production', () => {
425
+ const originalNodeEnv = process.env.NODE_ENV;
426
+
427
+ beforeEach(() => (process.env.NODE_ENV = 'production'));
428
+
429
+ afterEach(() => (process.env.NODE_ENV = originalNodeEnv));
430
+
431
+ itAddsJsEntrypointToWebComponent(
432
+ 'entrypoint without dev-server port',
433
+ () => `${baseUrl}/dist/bundle/light/${metadata.entrypoints!.light.js[0]}`
434
+ );
435
+ });
436
+ });
437
+
406
438
  describe('when entrypoint has module: true', () => {
407
439
  beforeEach(() => {
408
440
  metadata.entrypoints!.light.module = true;
@@ -0,0 +1,32 @@
1
+ /*
2
+ * Split from register-anvil2.test.tsx because register.tsx changed from
3
+ * dynamic require() to static `import * as` for Vite/ESM compatibility.
4
+ * The previous approach used jest.resetModules() mid-file to re-mock
5
+ * modules between tests, but static imports are resolved once at module
6
+ * load time and aren't affected by resetModules. Separate files give
7
+ * each test suite its own jest.mock declarations.
8
+ */
9
+ import '@testing-library/jest-dom';
10
+ import { within } from '@testing-library/react';
11
+ import {
12
+ createRegisterHarness,
13
+ optionalLibraryWrappers,
14
+ } from '../__mocks__/register-anvil2-harness';
15
+
16
+ jest.mock('@servicetitan/anvil2', () => ({}));
17
+ jest.mock('@servicetitan/design-system', () => ({}));
18
+
19
+ describe('[web-components] register', () => {
20
+ const { Page, subject } = createRegisterHarness('mock-no-optional-deps');
21
+
22
+ for (const [library, wrappers] of Object.entries(optionalLibraryWrappers)) {
23
+ describe(`when ${library} is not present`, () => {
24
+ test.each(wrappers)('does not wrap component within %s', name => {
25
+ subject();
26
+
27
+ const element = within(Page.componentShadowRoot as any).queryByText(`<${name}>`);
28
+ expect(element).not.toBeInTheDocument();
29
+ });
30
+ });
31
+ }
32
+ });
@@ -1,7 +1,11 @@
1
1
  import '@testing-library/jest-dom';
2
- import { render, within } from '@testing-library/react';
2
+ import { within } from '@testing-library/react';
3
3
  import { Fragment } from 'react';
4
- import { expectStylesInDom, MockComponent, registerComponent } from '../__mocks__';
4
+ import { expectStylesInDom } from '../__mocks__';
5
+ import {
6
+ createRegisterHarness,
7
+ optionalLibraryWrappers,
8
+ } from '../__mocks__/register-anvil2-harness';
5
9
 
6
10
  function mockComponent(name: string) {
7
11
  return {
@@ -37,30 +41,7 @@ jest.mock('@servicetitan/design-system', () => ({
37
41
  }));
38
42
 
39
43
  describe('[web-components] register', () => {
40
- const componentName = 'mock-anvil2-app';
41
-
42
- class Page {
43
- static get componentShadowRoot() {
44
- return document.querySelector(componentName)!.shadowRoot!;
45
- }
46
-
47
- static get portalShadowRoot() {
48
- return document.querySelector('body')!.lastElementChild!.shadowRoot!;
49
- }
50
- }
51
-
52
- beforeAll(() => {
53
- registerComponent(MockComponent, componentName, {
54
- sharedDependenciesNames: ['@servicetitan/anvil2'],
55
- });
56
- });
57
-
58
- const subject = () => {
59
- jest.spyOn(console, 'error').mockImplementation(jest.fn()); // suppress "ReactDOM.render deprecated" error
60
- const Component: any = componentName;
61
- const handleRef = (webComponent: any) => webComponent?.provide({ foo: 'bar' }); // triggers rendering
62
- return render(<Component ref={handleRef} />);
63
- };
44
+ const { Page, subject } = createRegisterHarness('mock-anvil2-app');
64
45
 
65
46
  describe('when Anvil2 bundle is present in the DOM', () => {
66
47
  let cssLink: HTMLLinkElement;
@@ -90,44 +71,10 @@ describe('[web-components] register', () => {
90
71
  });
91
72
  });
92
73
 
93
- function itWrapsComponentWithin(name: string, expected: boolean = true) {
94
- test(`${expected ? 'wraps' : 'does not wrap'} component within ${name}`, () => {
95
- subject();
96
-
97
- const element = within(Page.componentShadowRoot as any).queryByText(`<${name}>`);
98
- if (expected) {
99
- expect(element).toBeInTheDocument();
100
- } else {
101
- expect(element).not.toBeInTheDocument();
102
- }
103
- });
104
- }
105
-
106
- ['PortalProvider', 'DefaultPortalContext.Provider', 'TablePopupPropsContext.Provider'].forEach(
107
- name => itWrapsComponentWithin(name)
108
- );
109
-
110
- describe('when @servicetitan/anvil2 is not present', () => {
111
- beforeAll(() => {
112
- jest.resetModules();
113
- jest.mock('@servicetitan/anvil2', () => {
114
- throw new Error('Cannot find module');
115
- });
116
- });
117
-
118
- itWrapsComponentWithin('PortalProvider', false);
119
- });
120
-
121
- describe('when @servicetitan/design-system is not present', () => {
122
- beforeAll(() => {
123
- jest.resetModules();
124
- jest.mock('@servicetitan/design-system', () => {
125
- throw new Error('Cannot find module');
126
- });
127
- });
74
+ test.each(Object.values(optionalLibraryWrappers).flat())('wraps component within %s', name => {
75
+ subject();
128
76
 
129
- ['DefaultPortalContext.Provider', 'TablePopupPropsContext.Provider'].forEach(name =>
130
- itWrapsComponentWithin(name, false)
131
- );
77
+ const element = within(Page.componentShadowRoot as any).queryByText(`<${name}>`);
78
+ expect(element).toBeInTheDocument();
132
79
  });
133
80
  });