@vercel/microfrontends
Version:
Defines configuration and utilities for microfrontends development
1 lines • 26.7 kB
Source Map (JSON)
{"version":3,"sources":["../../src/next/client/index.ts","../../src/next/client/link/microfrontends-link.tsx","../../src/config/react/use-client-config.ts","../../src/config/microfrontends-config/client/index.ts","../../src/next/client/prefetch/prefetch-cross-zone-links.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links-context.tsx"],"sourcesContent":["export * from './link';\nexport * from './prefetch';\n","import NextLink, {\n type LinkProps as ExternalNextLinkProps,\n} from 'next/link.js';\nimport type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext, useMemo } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\n\n// fix for tsc inlining LinkProps from next\n// https://github.com/microsoft/TypeScript/issues/37151#issuecomment-756232934\ninterface NextLinkProps extends ExternalNextLinkProps {}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE_HASH = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;\n\nexport function useZoneForHref(href: LinkProps['href'] | undefined): {\n zoneOfHref: string | null;\n isDifferentZone: boolean;\n isLoading: boolean;\n} {\n const { clientConfig, isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const { isRelativePath, zoneOfHref } = useMemo(() => {\n const isRelative = typeof href === 'string' && href.startsWith('/');\n return {\n isRelativePath: isRelative,\n zoneOfHref: isRelative\n ? clientConfig.getApplicationNameForPath(href)\n : null,\n };\n }, [clientConfig, href]);\n\n if (typeof href === 'string' && !href.length) {\n return {\n zoneOfHref: null,\n isDifferentZone: false,\n isLoading: false,\n };\n }\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE_HASH !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with microfrontend set-ups and will prefetch the\n * cross zone links automatically.\n */\nexport const Link = forwardRef<HTMLAnchorElement, LinkProps>(\n ({ children, ...props }, ref): JSX.Element => {\n const { prefetchHref } = useContext(PrefetchCrossZoneLinksContext);\n const { zoneOfHref, isDifferentZone, isLoading } = useZoneForHref(\n props.href,\n );\n\n function onHoverPrefetch(): void {\n if (!props.href) {\n return;\n }\n prefetchHref(props.href);\n }\n\n if (isDifferentZone && zoneOfHref !== null) {\n const { prefetch: _, ...rest } = props;\n return (\n <a\n {...rest}\n data-zone={zoneOfHref}\n onFocus={props.prefetch !== false ? onHoverPrefetch : undefined}\n onMouseOver={props.prefetch !== false ? onHoverPrefetch : undefined}\n >\n {children}\n </a>\n );\n }\n\n return (\n <NextLink\n {...props}\n data-zone={!zoneOfHref ? 'null' : 'same'}\n prefetch={props.prefetch ?? (isLoading ? false : undefined)}\n ref={ref}\n >\n {children}\n </NextLink>\n );\n },\n);\nLink.displayName = 'MicrofrontendsLink';\n","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\nimport type { WellKnownClientData } from '../well-known/types';\n\nconst clientCache = new Map<string, MicrofrontendConfigClient>();\nconst cachedHasDynamicPaths = new Map<string, boolean>();\n\nconst getClient = (config: string | undefined) => {\n const existing = clientCache.get(config || '');\n if (existing) {\n return existing;\n }\n\n const client = MicrofrontendConfigClient.fromEnv(config);\n clientCache.set(config || '', client);\n return client;\n};\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n null;\n\nlet cachedServerClient: MicrofrontendConfigClient | null = null;\n\nasync function fetchClientConfigFromServer(): Promise<MicrofrontendConfigClient | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/microfrontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n const client = new MicrofrontendConfigClient(responseJson.config);\n cachedServerClient = client;\n return client;\n } catch (err) {\n return null;\n }\n}\n\n/**\n * Hook to use the client microfrontends configuration. This hook will resolve\n * dynamic paths by fetching the configuration from the server if necessary,\n * allowing the server to specify the values for dynamic paths.\n */\nexport function useClientConfig(config: string | undefined): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n () => cachedServerClient ?? getClient(config),\n );\n const canLoad = useMemo(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n return false;\n }\n // If we've already fetched the server config and it's resolved, we don't need\n // to enter the loading state at all\n if (cachedServerClient) return false;\n // If we've already checked this config for dynamic paths, we can use the\n // cached result from before instead of reevaluating.\n const existing = cachedHasDynamicPaths.get(config || '');\n if (existing !== undefined) return existing;\n // Get the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = getClient(config);\n // As an optimization, only fetch the config from the server if the\n // microfrontends configuration has any dynamic paths. If it doesn't,\n // then the server won't return any different values.\n const hasDynamicPaths = originalClientConfig.hasFlaggedPaths;\n cachedHasDynamicPaths.set(config || '', hasDynamicPaths);\n if (!hasDynamicPaths) {\n return false;\n }\n return true;\n }, [config]);\n const [isLoading, setIsLoading] = useState(canLoad);\n useEffect(() => {\n if (!canLoad) return;\n if (!cachedServerClientConfigPromise) {\n cachedServerClientConfigPromise = fetchClientConfigFromServer();\n }\n void cachedServerClientConfigPromise\n .then((newConfig) => {\n if (newConfig) {\n setClientConfig((prevConfig) => {\n return prevConfig.isEqual(newConfig) ? prevConfig : newConfig;\n });\n }\n })\n .finally(() => {\n setIsLoading(false);\n });\n }, [canLoad]);\n\n return { clientConfig, isLoading };\n}\n\nexport function resetCachedServerClientConfigPromise(): void {\n cachedServerClientConfigPromise = null;\n}\n","import { pathToRegexp } from 'path-to-regexp';\nimport type { ClientConfig } from './types';\n\nexport interface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nconst regexpCache = new Map<string, RegExp>();\nconst getRegexp = (path: string): RegExp => {\n const existing = regexpCache.get(path);\n if (existing) {\n return existing;\n }\n\n const regexp = pathToRegexp(path);\n regexpCache.set(path, regexp);\n return regexp;\n};\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n hasFlaggedPaths: boolean;\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.hasFlaggedPaths = config.hasFlaggedPaths ?? false;\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n if (app.routing.some((match) => match.flag)) {\n this.hasFlaggedPaths = true;\n }\n const newRouting = [];\n const pathsWithoutFlags = [];\n for (const group of app.routing) {\n if (group.flag) {\n if (opts?.removeFlaggedPaths) {\n continue;\n }\n if (group.group) {\n delete group.group;\n }\n newRouting.push(group);\n } else {\n pathsWithoutFlags.push(...group.paths);\n }\n }\n if (pathsWithoutFlags.length > 0) {\n newRouting.push({ paths: pathsWithoutFlags });\n }\n app.routing = newRouting;\n }\n }\n this.serialized = config;\n if (this.hasFlaggedPaths) {\n this.serialized.hasFlaggedPaths = this.hasFlaggedPaths;\n }\n this.applications = config.applications;\n }\n\n /**\n * Create a new `MicrofrontendConfigClient` from a JSON string.\n * Config must be passed in to remain framework agnostic\n */\n static fromEnv(config: string | undefined): MicrofrontendConfigClient {\n if (!config) {\n throw new Error(\n 'Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy',\n );\n }\n return new MicrofrontendConfigClient(JSON.parse(config) as ClientConfig);\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\n this === other ||\n JSON.stringify(this.applications) === JSON.stringify(other.applications)\n );\n }\n\n getApplicationNameForPath(path: string): string | null {\n if (!path.startsWith('/')) {\n throw new Error(`Path must start with a /`);\n }\n\n if (this.pathCache[path]) {\n return this.pathCache[path];\n }\n\n const pathname = new URL(path, 'https://example.com').pathname;\n for (const [name, application] of Object.entries(this.applications)) {\n if (application.routing) {\n for (const group of application.routing) {\n for (const childPath of group.paths) {\n const regexp = getRegexp(childPath);\n if (regexp.test(pathname)) {\n this.pathCache[path] = name;\n return name;\n }\n }\n }\n }\n }\n const defaultApplication = Object.entries(this.applications).find(\n ([, application]) => application.default,\n );\n if (!defaultApplication) {\n return null;\n }\n\n this.pathCache[path] = defaultApplication[0];\n return defaultApplication[0];\n }\n\n serialize(): ClientConfig {\n return this.serialized;\n }\n}\n","import Script from 'next/script.js';\nimport { useEffect, useState } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\n\nconst PREFETCH_ATTR = 'data-prefetch';\nconst DATA_ATTR_SELECTORS = {\n anyZone: '[data-zone]',\n external: '[data-zone=\"null\"]',\n sameZone: '[data-zone=\"same\"]',\n prefetch: `[${PREFETCH_ATTR}]`,\n} as const;\n\nconst PREFETCH_ON_HOVER_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n ],\n};\n\nconst PREFETCH_WHEN_VISIBLE_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n { selector_matches: DATA_ATTR_SELECTORS.prefetch },\n ],\n};\n\nfunction checkVisibility(element: Element | null): boolean {\n if (!element) return true;\n\n if ('checkVisibility' in element) {\n return element.checkVisibility({ opacityProperty: true });\n }\n\n // hack to get around TS thinking element is never;\n const el = element as Element;\n const style = window.getComputedStyle(el);\n\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n style.opacity === '0'\n ) {\n return false;\n }\n\n return checkVisibility(el.parentElement);\n}\n\ninterface PrefetchCrossZoneLinksProps {\n /**\n * This attributes controls how eager the browser should be in prerendering\n * cross-zone links. Prerendering downloads the HTML and subresources of the page\n * and starts to render the page in the background. This consumes more resources\n * but provides a faster user experience if the user decides to visit that page.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/speculationrules#eagerness\n * for more information.\n *\n * Default value is 'conservative'.\n */\n prerenderEagerness?: 'immediate' | 'eager' | 'moderate' | 'conservative';\n}\n\nexport function PrefetchCrossZoneLinks({\n prerenderEagerness = 'conservative',\n}: PrefetchCrossZoneLinksProps): JSX.Element | null {\n const { isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const [links, setLinks] = useState<HTMLAnchorElement[]>([]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Intersection observer to add the data-prefetch attribute to cross-zone\n * links that have yet to be prefetched and are visible.\n */\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (\n entry.isIntersecting &&\n !entry.target.hasAttribute(PREFETCH_ATTR) &&\n // lazy perform the visibility check for nodes that are intersecting the viewport\n // and have not been prefetched.\n checkVisibility(entry.target)\n ) {\n entry.target.setAttribute(PREFETCH_ATTR, 'true');\n }\n });\n },\n {\n root: null,\n rootMargin: '0px',\n threshold: 0.1,\n },\n );\n\n links.forEach((link) => observer.observe(link));\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading, links]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Mutation observer to notify when new nodes have entered/exited the document\n * or an href has changed.\n */\n const observer = new MutationObserver((mutations) => {\n const hasChanged = mutations.some((mutation) => {\n return (\n (mutation.type === 'childList' && mutation.addedNodes.length > 0) ||\n (mutation.type === 'attributes' && mutation.attributeName === 'href')\n );\n });\n\n if (hasChanged) {\n // Whenever there's a change, add all cross-zone links that haven't been\n // prefetched.\n setLinks(\n Array.from(\n document.querySelectorAll<HTMLAnchorElement>(\n `a${DATA_ATTR_SELECTORS.anyZone}:not(${DATA_ATTR_SELECTORS.prefetch}):not(${DATA_ATTR_SELECTORS.sameZone}):not(${DATA_ATTR_SELECTORS.external})`,\n ),\n ),\n );\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\n });\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading]);\n\n // Wait till the zone-config loads to take into consideration any\n // flagged routes.\n if (isLoading) {\n return null;\n }\n\n // Prefetch links with moderate eagerness by default, immediately when marked \"data-prefetch\".\n // Prerender links with conservative eagerness by default, immediately when marked \"data-prefetch\".\n const speculationRules = {\n prefetch: [\n {\n eagerness: 'moderate',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n {\n eagerness: 'immediate',\n where: PREFETCH_WHEN_VISIBLE_PREDICATES,\n },\n ],\n prerender: [\n {\n eagerness: prerenderEagerness,\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Safe - injecting JSON speculation rules, not HTML\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(speculationRules),\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n","import type React from 'react';\nimport {\n createContext,\n startTransition,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: {\n children: React.ReactNode;\n}): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const isSafariOrFirefox = useRef(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n\n // This useCallback must not have any dependencies because if it changes\n // its value, every component that uses this context will rerender.\n const prefetchHref = useCallback((href: string): void => {\n // It's not critical that we render the new preload `<link>` elements\n // immediately. We want to batch together `prefetchHref` calls that\n // occur in one synchronous pass and only render once after they've all\n // called this callback.\n startTransition(() => {\n setSeenHrefs((prevHrefs) => {\n if (prevHrefs.has(href)) return prevHrefs;\n return new Set(prevHrefs).add(href);\n });\n });\n }, []);\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox.current) {\n return <>{children}</>;\n }\n\n return (\n <PrefetchCrossZoneLinksContext.Provider value={value}>\n {children}\n {[...seenHrefs].map((href) => (\n <link as=\"fetch\" href={href} key={href} rel=\"preload\" />\n ))}\n </PrefetchCrossZoneLinksContext.Provider>\n );\n}\n"],"mappings":"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","names":["client_exports","__export","Link","PrefetchCrossZoneLinks","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","useZoneForHref","__toCommonJS","import_link","import_react","import_react","import_path_to_regexp","regexpCache","getRegexp","path","existing","regexp","MicrofrontendConfigClient","config","opts","app","match","newRouting","pathsWithoutFlags","group","other","pathname","name","application","childPath","defaultApplication","clientCache","cachedHasDynamicPaths","getClient","config","existing","client","MicrofrontendConfigClient","cachedServerClientConfigPromise","cachedServerClient","fetchClientConfigFromServer","response","responseJson","useClientConfig","clientConfig","setClientConfig","canLoad","hasDynamicPaths","isLoading","setIsLoading","newConfig","prevConfig","import_script","import_react","import_jsx_runtime","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","prerenderEagerness","isLoading","useClientConfig","links","setLinks","observer","entries","entry","link","mutations","mutation","Script","import_react","import_jsx_runtime","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","prefetchHref","href","prevHrefs","value","import_jsx_runtime","CURRENT_ZONE_HASH","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","isRelative","Link","children","props","ref","prefetchHref","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}