@vercel/microfrontends 0.13.0 → 0.14.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.
- package/README.md +3 -3
- package/dist/bin/cli.cjs +85 -60
- package/dist/config/client.d.ts +1 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js.map +1 -1
- package/dist/{index-83133f2d.d.ts → index-bf67a461.d.ts} +3 -10
- package/dist/next/client.cjs +1 -1
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/client.js.map +1 -1
- package/dist/next/endpoints.cjs.map +1 -1
- package/dist/next/endpoints.d.ts +1 -1
- package/dist/next/endpoints.js.map +1 -1
- package/dist/next/middleware.cjs +0 -29
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.js +0 -29
- package/dist/next/middleware.js.map +1 -1
- package/dist/{types-a995174e.d.ts → types-a29d224a.d.ts} +1 -7
- package/dist/{types-15b7f215.d.ts → types-cfe3308b.d.ts} +1 -1
- package/dist/types-fc30696d.d.ts +11 -0
- package/dist/utils/mfe-port.cjs +70 -56
- package/dist/utils/mfe-port.cjs.map +1 -1
- package/dist/utils/mfe-port.js +67 -53
- package/dist/utils/mfe-port.js.map +1 -1
- package/dist/v2/config.cjs +0 -14
- package/dist/v2/config.cjs.map +1 -1
- package/dist/v2/config.d.ts +4 -3
- package/dist/v2/config.js +0 -14
- package/dist/v2/config.js.map +1 -1
- package/dist/v2/microfrontends/server.cjs +68 -54
- package/dist/v2/microfrontends/server.cjs.map +1 -1
- package/dist/v2/microfrontends/server.d.ts +6 -5
- package/dist/v2/microfrontends/server.js +65 -51
- package/dist/v2/microfrontends/server.js.map +1 -1
- package/dist/v2/microfrontends.cjs +0 -14
- package/dist/v2/microfrontends.cjs.map +1 -1
- package/dist/v2/microfrontends.d.ts +4 -3
- package/dist/v2/microfrontends.js +0 -14
- package/dist/v2/microfrontends.js.map +1 -1
- package/dist/v2/next/client.cjs +1 -1
- package/dist/v2/next/client.cjs.map +1 -1
- package/dist/v2/next/client.js +1 -1
- package/dist/v2/next/client.js.map +1 -1
- package/dist/v2/next/config.cjs +106 -60
- package/dist/v2/next/config.cjs.map +1 -1
- package/dist/v2/next/config.js +103 -57
- package/dist/v2/next/config.js.map +1 -1
- package/dist/v2/next/endpoints.cjs.map +1 -1
- package/dist/v2/next/endpoints.d.ts +13 -2
- package/dist/v2/next/endpoints.js.map +1 -1
- package/dist/v2/next/middleware.cjs +10 -54
- package/dist/v2/next/middleware.cjs.map +1 -1
- package/dist/v2/next/middleware.d.ts +7 -2
- package/dist/v2/next/middleware.js +10 -54
- package/dist/v2/next/middleware.js.map +1 -1
- package/dist/v2/next/testing.cjs +992 -0
- package/dist/v2/next/testing.cjs.map +1 -0
- package/dist/v2/next/testing.d.ts +55 -0
- package/dist/v2/next/testing.js +961 -0
- package/dist/v2/next/testing.js.map +1 -0
- package/dist/v2/overrides.d.ts +3 -3
- package/dist/v2/routing.cjs +19 -0
- package/dist/v2/routing.cjs.map +1 -0
- package/dist/v2/routing.d.ts +26 -0
- package/dist/v2/routing.js +1 -0
- package/dist/v2/routing.js.map +1 -0
- package/dist/v2/schema.cjs.map +1 -1
- package/dist/v2/schema.d.ts +1 -1
- package/dist/validation.cjs +0 -4
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.ts +0 -6
- package/dist/validation.js +0 -4
- package/dist/validation.js.map +1 -1
- package/package.json +20 -5
- package/schema/schema-v2.json +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next-v2/client/index.ts","../../../src/next-v2/client/link/multi-zones-link.tsx","../../../src/config-v2/react/use-client-config.ts","../../../src/config-v2/microfrontends-config/client/index.ts","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links-context.tsx","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links.tsx"],"sourcesContent":["export * from './link';\nexport * from './prefetch';\n","import type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext } from 'react';\nimport NextLink, { type LinkProps as NextLinkProps } from 'next/link';\nimport { useClientConfig } from '../../../config-v2/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\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 removeFlaggedPathsFromDefault: true,\n },\n );\n const isRelativePath =\n typeof href === 'string' && (href as string).startsWith('/');\n const zoneOfHref = isRelativePath\n ? clientConfig.getApplicationNameForPath(href as string)\n : null;\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with Multi-Zones 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 return (\n // biome-ignore lint/a11y/useKeyWithMouseEvents: Only used for prefetching.\n // biome-ignore lint/nursery/noStaticElementInteractions: Ignored using `--suppress`\n <a\n {...props}\n data-zone={zoneOfHref}\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 = 'MultiZonesLink';\n","'use client';\n\nimport { useState, useEffect } from 'react';\nimport type { WellKnownClientData } from '../well-known/types';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n null;\n\nasync function fetchClientConfigFromServer(): Promise<MicrofrontendConfigClient | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/micro-frontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n return new MicrofrontendConfigClient(responseJson.config);\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(\n config: string | undefined,\n {\n removeFlaggedPathsFromDefault,\n }: {\n removeFlaggedPathsFromDefault?: boolean;\n } = {},\n): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n MicrofrontendConfigClient.fromEnv(config, {\n removeFlaggedPaths: removeFlaggedPathsFromDefault,\n }),\n );\n const [isLoading, setIsLoading] = useState(true);\n useEffect(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n setIsLoading(false);\n return;\n }\n // Since we may remove flagged paths from the client config above, we need\n // to use the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = MicrofrontendConfigClient.fromEnv(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 = Object.values(\n originalClientConfig.applications,\n ).some((app) => app.routing?.some((group) => group.flag));\n if (!hasDynamicPaths) {\n setIsLoading(false);\n return;\n }\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 }, [config, clientConfig.applications]);\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\ninterface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.serialized = config;\n if (opts?.removeFlaggedPaths) {\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n app.routing = app.routing.filter((match) => !match.flag);\n }\n }\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(\n config: string | undefined,\n opts?: MicrofrontendConfigClientOptions,\n ): MicrofrontendConfigClient {\n if (!config) {\n throw new Error('No microfrontends configuration found');\n }\n return new MicrofrontendConfigClient(\n JSON.parse(config) as ClientConfig,\n opts,\n );\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\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 = pathToRegexp(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 React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: { children: React.ReactNode }): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const [isSafariOrFirefox, setIsSafariOrFirefox] = useState(false);\n\n useEffect(() => {\n setIsSafariOrFirefox(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n }, []);\n\n const prefetchHref = useCallback(\n (href: string): void => {\n if (!seenHrefs.has(href)) {\n setSeenHrefs(new Set(seenHrefs).add(href));\n }\n },\n [seenHrefs],\n );\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox) {\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","import { useEffect, useState } from 'react';\nimport Script from 'next/script';\nimport { useClientConfig } from '../../../config-v2/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\nexport function PrefetchCrossZoneLinks(): 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: 'conservative',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n dangerouslySetInnerHTML={{\n __html: `${JSON.stringify(speculationRules)}`,\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n"],"mappings":"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","names":["client_exports","__export","Link","PrefetchCrossZoneLinks","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","useZoneForHref","__toCommonJS","import_react","import_link","import_react","import_path_to_regexp","MicrofrontendConfigClient","config","opts","app","match","other","path","pathname","name","application","group","childPath","defaultApplication","cachedServerClientConfigPromise","fetchClientConfigFromServer","response","responseJson","MicrofrontendConfigClient","useClientConfig","config","removeFlaggedPathsFromDefault","clientConfig","setClientConfig","isLoading","setIsLoading","originalClientConfig","app","_a","group","newConfig","prevConfig","import_react","import_jsx_runtime","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","setIsSafariOrFirefox","prefetchHref","href","value","import_react","import_script","import_jsx_runtime","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","isLoading","useClientConfig","links","setLinks","observer","entries","entry","link","mutations","mutation","Script","import_jsx_runtime","CURRENT_ZONE","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","Link","children","props","ref","prefetchHref","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","NextLink"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/next-v2/client/index.ts","../../../src/next-v2/client/link/multi-zones-link.tsx","../../../src/config-v2/react/use-client-config.ts","../../../src/config-v2/microfrontends-config/client/index.ts","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links-context.tsx","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links.tsx"],"sourcesContent":["export * from './link';\nexport * from './prefetch';\n","import type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext } from 'react';\nimport NextLink, { type LinkProps as NextLinkProps } from 'next/link';\nimport { useClientConfig } from '../../../config-v2/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\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 removeFlaggedPathsFromDefault: true,\n },\n );\n const isRelativePath =\n typeof href === 'string' && (href as string).startsWith('/');\n const zoneOfHref = isRelativePath\n ? clientConfig.getApplicationNameForPath(href as string)\n : null;\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with Multi-Zones 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 // biome-ignore lint/a11y/useKeyWithMouseEvents: Only used for prefetching.\n // biome-ignore lint/nursery/noStaticElementInteractions: Ignored using `--suppress`\n <a\n {...rest}\n data-zone={zoneOfHref}\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 = 'MultiZonesLink';\n","'use client';\n\nimport { useState, useEffect } from 'react';\nimport type { WellKnownClientData } from '../well-known/types';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n 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 return new MicrofrontendConfigClient(responseJson.config);\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(\n config: string | undefined,\n {\n removeFlaggedPathsFromDefault,\n }: {\n removeFlaggedPathsFromDefault?: boolean;\n } = {},\n): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n MicrofrontendConfigClient.fromEnv(config, {\n removeFlaggedPaths: removeFlaggedPathsFromDefault,\n }),\n );\n const [isLoading, setIsLoading] = useState(true);\n useEffect(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n setIsLoading(false);\n return;\n }\n // Since we may remove flagged paths from the client config above, we need\n // to use the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = MicrofrontendConfigClient.fromEnv(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 = Object.values(\n originalClientConfig.applications,\n ).some((app) => app.routing?.some((group) => group.flag));\n if (!hasDynamicPaths) {\n setIsLoading(false);\n return;\n }\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 }, [config, clientConfig.applications]);\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\ninterface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.serialized = config;\n if (opts?.removeFlaggedPaths) {\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n app.routing = app.routing.filter((match) => !match.flag);\n }\n }\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(\n config: string | undefined,\n opts?: MicrofrontendConfigClientOptions,\n ): MicrofrontendConfigClient {\n if (!config) {\n throw new Error('No microfrontends configuration found');\n }\n return new MicrofrontendConfigClient(\n JSON.parse(config) as ClientConfig,\n opts,\n );\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\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 = pathToRegexp(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 React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: { children: React.ReactNode }): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const [isSafariOrFirefox, setIsSafariOrFirefox] = useState(false);\n\n useEffect(() => {\n setIsSafariOrFirefox(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n }, []);\n\n const prefetchHref = useCallback(\n (href: string): void => {\n if (!seenHrefs.has(href)) {\n setSeenHrefs(new Set(seenHrefs).add(href));\n }\n },\n [seenHrefs],\n );\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox) {\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","import { useEffect, useState } from 'react';\nimport Script from 'next/script';\nimport { useClientConfig } from '../../../config-v2/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\nexport function PrefetchCrossZoneLinks(): 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: 'conservative',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n dangerouslySetInnerHTML={{\n __html: `${JSON.stringify(speculationRules)}`,\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n"],"mappings":"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","names":["client_exports","__export","Link","PrefetchCrossZoneLinks","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","useZoneForHref","__toCommonJS","import_react","import_link","import_react","import_path_to_regexp","MicrofrontendConfigClient","config","opts","app","match","other","path","pathname","name","application","group","childPath","defaultApplication","cachedServerClientConfigPromise","fetchClientConfigFromServer","response","responseJson","MicrofrontendConfigClient","useClientConfig","config","removeFlaggedPathsFromDefault","clientConfig","setClientConfig","isLoading","setIsLoading","originalClientConfig","app","_a","group","newConfig","prevConfig","import_react","import_jsx_runtime","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","setIsSafariOrFirefox","prefetchHref","href","value","import_react","import_script","import_jsx_runtime","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","isLoading","useClientConfig","links","setLinks","observer","entries","entry","link","mutations","mutation","Script","import_jsx_runtime","CURRENT_ZONE","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","Link","children","props","ref","prefetchHref","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}
|
package/dist/v2/next/client.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{forwardRef as Z,useContext as I}from"react";import D from"next/link";import{useState as C,useEffect as b}from"react";import{pathToRegexp as N}from"path-to-regexp";var c=class{constructor(e,r){this.pathCache={};if(this.serialized=e,r!=null&&r.removeFlaggedPaths)for(let i of Object.values(e.applications))i.routing&&(i.routing=i.routing.filter(n=>!n.flag));this.applications=e.applications}static fromEnv(e,r){if(!e)throw new Error("No microfrontends configuration found");return new c(JSON.parse(e),r)}isEqual(e){return JSON.stringify(this.applications)===JSON.stringify(e.applications)}getApplicationNameForPath(e){if(!e.startsWith("/"))throw new Error("Path must start with a /");if(this.pathCache[e])return this.pathCache[e];let r=new URL(e,"https://example.com").pathname;for(let[n,o]of Object.entries(this.applications))if(o.routing){for(let a of o.routing)for(let s of a.paths)if(N(s).test(r))return this.pathCache[e]=n,n}let i=Object.entries(this.applications).find(([,n])=>n.default);return i?(this.pathCache[e]=i[0],i[0]):null}serialize(){return this.serialized}};var h=null;async function x(){try{let t=await fetch("/.well-known/vercel/
|
|
2
|
+
import{forwardRef as Z,useContext as I}from"react";import D from"next/link";import{useState as C,useEffect as b}from"react";import{pathToRegexp as N}from"path-to-regexp";var c=class{constructor(e,r){this.pathCache={};if(this.serialized=e,r!=null&&r.removeFlaggedPaths)for(let i of Object.values(e.applications))i.routing&&(i.routing=i.routing.filter(n=>!n.flag));this.applications=e.applications}static fromEnv(e,r){if(!e)throw new Error("No microfrontends configuration found");return new c(JSON.parse(e),r)}isEqual(e){return JSON.stringify(this.applications)===JSON.stringify(e.applications)}getApplicationNameForPath(e){if(!e.startsWith("/"))throw new Error("Path must start with a /");if(this.pathCache[e])return this.pathCache[e];let r=new URL(e,"https://example.com").pathname;for(let[n,o]of Object.entries(this.applications))if(o.routing){for(let a of o.routing)for(let s of a.paths)if(N(s).test(r))return this.pathCache[e]=n,n}let i=Object.entries(this.applications).find(([,n])=>n.default);return i?(this.pathCache[e]=i[0],i[0]):null}serialize(){return this.serialized}};var h=null;async function x(){try{let t=await fetch("/.well-known/vercel/microfrontends/client-config");if(t.status!==200)return null;let e=await t.json();return new c(e.config)}catch{return null}}function p(t,{removeFlaggedPathsFromDefault:e}={}){let[r,i]=C(c.fromEnv(t,{removeFlaggedPaths:e})),[n,o]=C(!0);return b(()=>{if(process.env.NODE_ENV==="test"&&process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER!=="1"){o(!1);return}let a=c.fromEnv(t);if(!Object.values(a.applications).some(l=>{var u;return(u=l.routing)==null?void 0:u.some(_=>_.flag)})){o(!1);return}h||(h=x()),h.then(l=>{l&&i(u=>u.isEqual(l)?u:l)}).finally(()=>{o(!1)})},[t,r.applications]),{clientConfig:r,isLoading:n}}import{createContext as O,useCallback as k,useEffect as R,useMemo as S,useState as g}from"react";import{Fragment as T,jsx as E,jsxs as F}from"react/jsx-runtime";var d=O({prefetchHref:()=>{}});function j({children:t}){let[e,r]=g(new Set),[i,n]=g(!1);R(()=>{n(typeof navigator<"u"&&(navigator.userAgent.includes("Firefox")||navigator.userAgent.includes("Safari")&&!navigator.userAgent.includes("Chrome")))},[]);let o=k(s=>{e.has(s)||r(new Set(e).add(s))},[e]),a=S(()=>({prefetchHref:o}),[o]);return i?F(d.Provider,{value:a,children:[t,[...e].map(s=>E("link",{as:"fetch",href:s,rel:"preload"},s))]}):E(T,{children:t})}import{useEffect as P,useState as A}from"react";import M from"next/script";import{jsx as H}from"react/jsx-runtime";var m="data-prefetch",f={anyZone:"[data-zone]",external:'[data-zone="null"]',sameZone:'[data-zone="same"]',prefetch:`[${m}]`},v={and:[{href_matches:"/*"},{selector_matches:f.anyZone},{not:{selector_matches:f.sameZone}},{not:{selector_matches:f.external}}]},w={and:[{href_matches:"/*"},{selector_matches:f.anyZone},{not:{selector_matches:f.sameZone}},{not:{selector_matches:f.external}},{selector_matches:f.prefetch}]};function L(t){if(!t)return!0;if("checkVisibility"in t)return t.checkVisibility({opacityProperty:!0});let e=t,r=window.getComputedStyle(e);return r.display==="none"||r.visibility==="hidden"||r.opacity==="0"?!1:L(e.parentElement)}function ne(){let{isLoading:t}=p(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),[e,r]=A([]);return P(()=>{if(t)return;let n=new IntersectionObserver(o=>{o.forEach(a=>{a.isIntersecting&&!a.target.hasAttribute(m)&&L(a.target)&&a.target.setAttribute(m,"true")})},{root:null,rootMargin:"0px",threshold:.1});return e.forEach(o=>n.observe(o)),()=>{n.disconnect()}},[t,e]),P(()=>{if(t)return;let n=new MutationObserver(o=>{o.some(s=>s.type==="childList"&&s.addedNodes.length>0||s.type==="attributes"&&s.attributeName==="href")&&r(Array.from(document.querySelectorAll(`a${f.anyZone}:not(${f.prefetch}):not(${f.sameZone}):not(${f.external})`)))});return n.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["href"]}),()=>{n.disconnect()}},[t]),t?null:H(M,{dangerouslySetInnerHTML:{__html:`${JSON.stringify({prefetch:[{eagerness:"moderate",where:v},{eagerness:"immediate",where:w}],prerender:[{eagerness:"conservative",where:v}]})}`},id:"prefetch-zones-links",type:"speculationrules"})}import{jsx as y}from"react/jsx-runtime";var z=process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;function B(t){let{clientConfig:e,isLoading:r}=p(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,{removeFlaggedPathsFromDefault:!0}),i=typeof t=="string"&&t.startsWith("/"),n=i?e.getApplicationNameForPath(t):null;return{zoneOfHref:n,isDifferentZone:!i||(n?z!==n:!1),isLoading:r}}var J=Z(({children:t,...e},r)=>{let{prefetchHref:i}=I(d),{zoneOfHref:n,isDifferentZone:o,isLoading:a}=B(e.href);function s(){e.href&&i(e.href)}if(o&&n!==null){let{prefetch:l,...u}=e;return y("a",{...u,"data-zone":n,onMouseOver:e.prefetch!==!1?s:void 0,children:t})}return y(D,{...e,"data-zone":n?"same":"null",prefetch:e.prefetch??(a?!1:void 0),ref:r,children:t})});J.displayName="MultiZonesLink";export{J as Link,ne as PrefetchCrossZoneLinks,d as PrefetchCrossZoneLinksContext,j as PrefetchCrossZoneLinksProvider,B as useZoneForHref};
|
|
3
3
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next-v2/client/link/multi-zones-link.tsx","../../../src/config-v2/react/use-client-config.ts","../../../src/config-v2/microfrontends-config/client/index.ts","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links-context.tsx","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links.tsx"],"sourcesContent":["import type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext } from 'react';\nimport NextLink, { type LinkProps as NextLinkProps } from 'next/link';\nimport { useClientConfig } from '../../../config-v2/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\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 removeFlaggedPathsFromDefault: true,\n },\n );\n const isRelativePath =\n typeof href === 'string' && (href as string).startsWith('/');\n const zoneOfHref = isRelativePath\n ? clientConfig.getApplicationNameForPath(href as string)\n : null;\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with Multi-Zones 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 return (\n // biome-ignore lint/a11y/useKeyWithMouseEvents: Only used for prefetching.\n // biome-ignore lint/nursery/noStaticElementInteractions: Ignored using `--suppress`\n <a\n {...props}\n data-zone={zoneOfHref}\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 = 'MultiZonesLink';\n","'use client';\n\nimport { useState, useEffect } from 'react';\nimport type { WellKnownClientData } from '../well-known/types';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n null;\n\nasync function fetchClientConfigFromServer(): Promise<MicrofrontendConfigClient | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/micro-frontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n return new MicrofrontendConfigClient(responseJson.config);\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(\n config: string | undefined,\n {\n removeFlaggedPathsFromDefault,\n }: {\n removeFlaggedPathsFromDefault?: boolean;\n } = {},\n): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n MicrofrontendConfigClient.fromEnv(config, {\n removeFlaggedPaths: removeFlaggedPathsFromDefault,\n }),\n );\n const [isLoading, setIsLoading] = useState(true);\n useEffect(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n setIsLoading(false);\n return;\n }\n // Since we may remove flagged paths from the client config above, we need\n // to use the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = MicrofrontendConfigClient.fromEnv(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 = Object.values(\n originalClientConfig.applications,\n ).some((app) => app.routing?.some((group) => group.flag));\n if (!hasDynamicPaths) {\n setIsLoading(false);\n return;\n }\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 }, [config, clientConfig.applications]);\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\ninterface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.serialized = config;\n if (opts?.removeFlaggedPaths) {\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n app.routing = app.routing.filter((match) => !match.flag);\n }\n }\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(\n config: string | undefined,\n opts?: MicrofrontendConfigClientOptions,\n ): MicrofrontendConfigClient {\n if (!config) {\n throw new Error('No microfrontends configuration found');\n }\n return new MicrofrontendConfigClient(\n JSON.parse(config) as ClientConfig,\n opts,\n );\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\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 = pathToRegexp(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 React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: { children: React.ReactNode }): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const [isSafariOrFirefox, setIsSafariOrFirefox] = useState(false);\n\n useEffect(() => {\n setIsSafariOrFirefox(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n }, []);\n\n const prefetchHref = useCallback(\n (href: string): void => {\n if (!seenHrefs.has(href)) {\n setSeenHrefs(new Set(seenHrefs).add(href));\n }\n },\n [seenHrefs],\n );\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox) {\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","import { useEffect, useState } from 'react';\nimport Script from 'next/script';\nimport { useClientConfig } from '../../../config-v2/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\nexport function PrefetchCrossZoneLinks(): 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: 'conservative',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n dangerouslySetInnerHTML={{\n __html: `${JSON.stringify(speculationRules)}`,\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n"],"mappings":"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","names":["forwardRef","useContext","NextLink","useState","useEffect","pathToRegexp","MicrofrontendConfigClient","config","opts","app","match","other","path","pathname","name","application","group","childPath","defaultApplication","cachedServerClientConfigPromise","fetchClientConfigFromServer","response","responseJson","MicrofrontendConfigClient","useClientConfig","config","removeFlaggedPathsFromDefault","clientConfig","setClientConfig","useState","isLoading","setIsLoading","useEffect","originalClientConfig","app","_a","group","newConfig","prevConfig","createContext","useCallback","useEffect","useMemo","useState","Fragment","jsx","jsxs","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","setIsSafariOrFirefox","prefetchHref","href","value","useEffect","useState","Script","jsx","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","isLoading","useClientConfig","links","setLinks","useState","useEffect","observer","entries","entry","link","mutations","mutation","Script","jsx","CURRENT_ZONE","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","Link","forwardRef","children","props","ref","prefetchHref","useContext","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","NextLink"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/next-v2/client/link/multi-zones-link.tsx","../../../src/config-v2/react/use-client-config.ts","../../../src/config-v2/microfrontends-config/client/index.ts","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links-context.tsx","../../../src/next-v2/client/prefetch/prefetch-cross-zone-links.tsx"],"sourcesContent":["import type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext } from 'react';\nimport NextLink, { type LinkProps as NextLinkProps } from 'next/link';\nimport { useClientConfig } from '../../../config-v2/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\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 removeFlaggedPathsFromDefault: true,\n },\n );\n const isRelativePath =\n typeof href === 'string' && (href as string).startsWith('/');\n const zoneOfHref = isRelativePath\n ? clientConfig.getApplicationNameForPath(href as string)\n : null;\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with Multi-Zones 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 // biome-ignore lint/a11y/useKeyWithMouseEvents: Only used for prefetching.\n // biome-ignore lint/nursery/noStaticElementInteractions: Ignored using `--suppress`\n <a\n {...rest}\n data-zone={zoneOfHref}\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 = 'MultiZonesLink';\n","'use client';\n\nimport { useState, useEffect } from 'react';\nimport type { WellKnownClientData } from '../well-known/types';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n 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 return new MicrofrontendConfigClient(responseJson.config);\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(\n config: string | undefined,\n {\n removeFlaggedPathsFromDefault,\n }: {\n removeFlaggedPathsFromDefault?: boolean;\n } = {},\n): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n MicrofrontendConfigClient.fromEnv(config, {\n removeFlaggedPaths: removeFlaggedPathsFromDefault,\n }),\n );\n const [isLoading, setIsLoading] = useState(true);\n useEffect(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n setIsLoading(false);\n return;\n }\n // Since we may remove flagged paths from the client config above, we need\n // to use the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = MicrofrontendConfigClient.fromEnv(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 = Object.values(\n originalClientConfig.applications,\n ).some((app) => app.routing?.some((group) => group.flag));\n if (!hasDynamicPaths) {\n setIsLoading(false);\n return;\n }\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 }, [config, clientConfig.applications]);\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\ninterface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.serialized = config;\n if (opts?.removeFlaggedPaths) {\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n app.routing = app.routing.filter((match) => !match.flag);\n }\n }\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(\n config: string | undefined,\n opts?: MicrofrontendConfigClientOptions,\n ): MicrofrontendConfigClient {\n if (!config) {\n throw new Error('No microfrontends configuration found');\n }\n return new MicrofrontendConfigClient(\n JSON.parse(config) as ClientConfig,\n opts,\n );\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\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 = pathToRegexp(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 React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: { children: React.ReactNode }): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const [isSafariOrFirefox, setIsSafariOrFirefox] = useState(false);\n\n useEffect(() => {\n setIsSafariOrFirefox(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n }, []);\n\n const prefetchHref = useCallback(\n (href: string): void => {\n if (!seenHrefs.has(href)) {\n setSeenHrefs(new Set(seenHrefs).add(href));\n }\n },\n [seenHrefs],\n );\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox) {\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","import { useEffect, useState } from 'react';\nimport Script from 'next/script';\nimport { useClientConfig } from '../../../config-v2/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\nexport function PrefetchCrossZoneLinks(): 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: 'conservative',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n dangerouslySetInnerHTML={{\n __html: `${JSON.stringify(speculationRules)}`,\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n"],"mappings":"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","names":["forwardRef","useContext","NextLink","useState","useEffect","pathToRegexp","MicrofrontendConfigClient","config","opts","app","match","other","path","pathname","name","application","group","childPath","defaultApplication","cachedServerClientConfigPromise","fetchClientConfigFromServer","response","responseJson","MicrofrontendConfigClient","useClientConfig","config","removeFlaggedPathsFromDefault","clientConfig","setClientConfig","useState","isLoading","setIsLoading","useEffect","originalClientConfig","app","_a","group","newConfig","prevConfig","createContext","useCallback","useEffect","useMemo","useState","Fragment","jsx","jsxs","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","setIsSafariOrFirefox","prefetchHref","href","value","useEffect","useState","Script","jsx","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","isLoading","useClientConfig","links","setLinks","useState","useEffect","observer","entries","entry","link","mutations","mutation","Script","jsx","CURRENT_ZONE","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","Link","forwardRef","children","props","ref","prefetchHref","useContext","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}
|
package/dist/v2/next/config.cjs
CHANGED
|
@@ -33,11 +33,11 @@ __export(config_exports, {
|
|
|
33
33
|
withMicrofrontends: () => withMicrofrontends
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(config_exports);
|
|
36
|
-
var
|
|
36
|
+
var import_node_fs8 = __toESM(require("fs"), 1);
|
|
37
37
|
|
|
38
38
|
// src/config-v2/microfrontends/server/index.ts
|
|
39
|
-
var
|
|
40
|
-
var
|
|
39
|
+
var import_node_fs7 = __toESM(require("fs"), 1);
|
|
40
|
+
var import_node_path8 = require("path");
|
|
41
41
|
|
|
42
42
|
// src/config-v2/overrides/constants.ts
|
|
43
43
|
var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
|
|
@@ -384,19 +384,6 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
384
384
|
);
|
|
385
385
|
}
|
|
386
386
|
};
|
|
387
|
-
var validateConfigOptions = (options) => {
|
|
388
|
-
var _a;
|
|
389
|
-
if ((_a = options == null ? void 0 : options.vercel) == null ? void 0 : _a.previewDeploymentSuffix) {
|
|
390
|
-
if (!/^[a-zA-Z]{2,}\.[a-zA-Z]{2,}$/.test(
|
|
391
|
-
options.vercel.previewDeploymentSuffix
|
|
392
|
-
)) {
|
|
393
|
-
throw new MicrofrontendError(
|
|
394
|
-
`Invalid preview deployment suffix: ${options.vercel.previewDeploymentSuffix}. Should have be formatted like "vercel.app".`,
|
|
395
|
-
{ type: "config", subtype: "invalid_preview_deployment_suffix" }
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
387
|
|
|
401
388
|
// src/config-v2/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
402
389
|
var PREFIX = "vc-ap";
|
|
@@ -615,7 +602,6 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
615
602
|
validateConfigPaths(c.applications);
|
|
616
603
|
validateConfigDefaultApplication(c.applications);
|
|
617
604
|
}
|
|
618
|
-
validateConfigOptions(c.options);
|
|
619
605
|
return c;
|
|
620
606
|
}
|
|
621
607
|
static fromEnv({
|
|
@@ -891,29 +877,39 @@ function findPackagePath(opts) {
|
|
|
891
877
|
// src/config-v2/microfrontends/utils/find-default-package.ts
|
|
892
878
|
var import_node_path3 = require("path");
|
|
893
879
|
var import_node_fs3 = require("fs");
|
|
880
|
+
var import_jsonc_parser2 = require("jsonc-parser");
|
|
894
881
|
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
882
|
+
|
|
883
|
+
// src/config-v2/constants.ts
|
|
884
|
+
var CONFIGURATION_FILENAMES = [
|
|
885
|
+
"microfrontends.jsonc",
|
|
886
|
+
"microfrontends.json"
|
|
887
|
+
];
|
|
888
|
+
|
|
889
|
+
// src/config-v2/microfrontends/utils/find-default-package.ts
|
|
895
890
|
var configCache2 = {};
|
|
896
891
|
function findDefaultMicrofrontendsPackages({
|
|
897
892
|
repositoryRoot,
|
|
898
893
|
applicationName
|
|
899
894
|
}) {
|
|
900
895
|
try {
|
|
901
|
-
const microfrontendsJsonPaths = import_fast_glob2.default.globSync(
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
896
|
+
const microfrontendsJsonPaths = import_fast_glob2.default.globSync(
|
|
897
|
+
`**/{${CONFIGURATION_FILENAMES.join(",")}}`,
|
|
898
|
+
{
|
|
899
|
+
cwd: repositoryRoot,
|
|
900
|
+
absolute: true,
|
|
901
|
+
onlyFiles: true,
|
|
902
|
+
followSymbolicLinks: false,
|
|
903
|
+
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
904
|
+
}
|
|
905
|
+
);
|
|
908
906
|
const matchingPaths = [];
|
|
909
907
|
for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
|
|
910
908
|
const microfrontendsJsonContent = (0, import_node_fs3.readFileSync)(
|
|
911
909
|
microfrontendsJsonPath,
|
|
912
910
|
"utf-8"
|
|
913
911
|
);
|
|
914
|
-
const microfrontendsJson =
|
|
915
|
-
microfrontendsJsonContent
|
|
916
|
-
);
|
|
912
|
+
const microfrontendsJson = (0, import_jsonc_parser2.parse)(microfrontendsJsonContent);
|
|
917
913
|
if (isMainConfig(microfrontendsJson) && microfrontendsJson.applications[applicationName]) {
|
|
918
914
|
matchingPaths.push(microfrontendsJsonPath);
|
|
919
915
|
}
|
|
@@ -943,7 +939,7 @@ function findDefaultMicrofrontendsPackage(opts) {
|
|
|
943
939
|
const result = findDefaultMicrofrontendsPackages(opts);
|
|
944
940
|
if (!result) {
|
|
945
941
|
throw new Error(
|
|
946
|
-
`Error trying to resolve the main microfrontends
|
|
942
|
+
`Error trying to resolve the main microfrontends configuration`
|
|
947
943
|
);
|
|
948
944
|
}
|
|
949
945
|
configCache2[cacheKey] = result;
|
|
@@ -995,8 +991,21 @@ function findPackageRoot(startDir) {
|
|
|
995
991
|
);
|
|
996
992
|
}
|
|
997
993
|
|
|
994
|
+
// src/config-v2/microfrontends/utils/find-config.ts
|
|
995
|
+
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
996
|
+
var import_node_path6 = require("path");
|
|
997
|
+
function findConfig({ dir }) {
|
|
998
|
+
for (const filename of CONFIGURATION_FILENAMES) {
|
|
999
|
+
const maybeConfig = (0, import_node_path6.join)(dir, filename);
|
|
1000
|
+
if (import_node_fs6.default.existsSync(maybeConfig)) {
|
|
1001
|
+
return maybeConfig;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
998
1007
|
// src/config-v2/microfrontends/server/utils/get-output-file-path.ts
|
|
999
|
-
var
|
|
1008
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
1000
1009
|
|
|
1001
1010
|
// src/config-v2/microfrontends/server/constants.ts
|
|
1002
1011
|
var MFE_CONFIG_DEFAULT_FILE_PATH = "microfrontends";
|
|
@@ -1010,17 +1019,17 @@ function isVercel() {
|
|
|
1010
1019
|
// src/config-v2/microfrontends/server/utils/get-output-file-path.ts
|
|
1011
1020
|
function getOutputFilePath() {
|
|
1012
1021
|
if (isVercel()) {
|
|
1013
|
-
return
|
|
1022
|
+
return import_node_path7.default.join(
|
|
1014
1023
|
".vercel",
|
|
1015
1024
|
MFE_CONFIG_DEFAULT_FILE_PATH,
|
|
1016
1025
|
MFE_CONFIG_DEFAULT_FILE_NAME
|
|
1017
1026
|
);
|
|
1018
1027
|
}
|
|
1019
|
-
return
|
|
1028
|
+
return import_node_path7.default.join(MFE_CONFIG_DEFAULT_FILE_PATH, MFE_CONFIG_DEFAULT_FILE_NAME);
|
|
1020
1029
|
}
|
|
1021
1030
|
|
|
1022
1031
|
// src/config-v2/microfrontends/server/validation.ts
|
|
1023
|
-
var
|
|
1032
|
+
var import_jsonc_parser3 = require("jsonc-parser");
|
|
1024
1033
|
var import_ajv = require("ajv");
|
|
1025
1034
|
|
|
1026
1035
|
// src/config/errors.ts
|
|
@@ -1173,10 +1182,6 @@ var schema_v2_default = {
|
|
|
1173
1182
|
VercelOptions: {
|
|
1174
1183
|
type: "object",
|
|
1175
1184
|
properties: {
|
|
1176
|
-
previewDeploymentSuffix: {
|
|
1177
|
-
type: "string",
|
|
1178
|
-
description: "If your team uses a custom Preview Deployment Suffix, please specify it here. See https://vercel.com/docs/deployments/preview-deployment-suffix. The default is `vercel.app`."
|
|
1179
|
-
},
|
|
1180
1185
|
teamSlug: {
|
|
1181
1186
|
type: "string",
|
|
1182
1187
|
description: "Team slug for the Vercel team"
|
|
@@ -1370,7 +1375,7 @@ var SCHEMA = schema_v2_default;
|
|
|
1370
1375
|
|
|
1371
1376
|
// src/config-v2/microfrontends/server/validation.ts
|
|
1372
1377
|
function validateSchema(configString) {
|
|
1373
|
-
const parsedConfig = (0,
|
|
1378
|
+
const parsedConfig = (0, import_jsonc_parser3.parse)(configString);
|
|
1374
1379
|
const ajv = new import_ajv.Ajv();
|
|
1375
1380
|
const validate = ajv.compile(SCHEMA);
|
|
1376
1381
|
const isValid = validate(parsedConfig);
|
|
@@ -1392,8 +1397,8 @@ var MicrofrontendsServer = class extends Microfrontends {
|
|
|
1392
1397
|
pretty: true
|
|
1393
1398
|
}) {
|
|
1394
1399
|
const outputPath = getOutputFilePath();
|
|
1395
|
-
|
|
1396
|
-
|
|
1400
|
+
import_node_fs7.default.mkdirSync((0, import_node_path8.dirname)(outputPath), { recursive: true });
|
|
1401
|
+
import_node_fs7.default.writeFileSync(
|
|
1397
1402
|
outputPath,
|
|
1398
1403
|
JSON.stringify(
|
|
1399
1404
|
this.config.toSchemaJson(),
|
|
@@ -1456,8 +1461,8 @@ var MicrofrontendsServer = class extends Microfrontends {
|
|
|
1456
1461
|
return config;
|
|
1457
1462
|
}
|
|
1458
1463
|
/**
|
|
1459
|
-
* Looks up the configuration by inferring the package root and looking for a microfrontends
|
|
1460
|
-
* it will look for a package in the repository with a microfrontends
|
|
1464
|
+
* Looks up the configuration by inferring the package root and looking for a microfrontends config file. If a file is not found,
|
|
1465
|
+
* it will look for a package in the repository with a microfrontends file that contains the current application
|
|
1461
1466
|
* and use that configuration.
|
|
1462
1467
|
*
|
|
1463
1468
|
* This can return either a Child or Main configuration.
|
|
@@ -1479,16 +1484,16 @@ var MicrofrontendsServer = class extends Microfrontends {
|
|
|
1479
1484
|
}
|
|
1480
1485
|
try {
|
|
1481
1486
|
const packageRoot = findPackageRoot(directory);
|
|
1482
|
-
const packageJsonPath = (0,
|
|
1487
|
+
const packageJsonPath = (0, import_node_path8.join)(packageRoot, "package.json");
|
|
1483
1488
|
const packageJson = JSON.parse(
|
|
1484
|
-
|
|
1489
|
+
import_node_fs7.default.readFileSync(packageJsonPath, "utf-8")
|
|
1485
1490
|
);
|
|
1486
1491
|
if (!packageJson.name) {
|
|
1487
1492
|
throw new Error(`No name found in package.json at ${packageJsonPath}`);
|
|
1488
1493
|
}
|
|
1489
1494
|
const configMeta = meta ?? { fromApp: packageJson.name };
|
|
1490
|
-
const maybeConfig = (
|
|
1491
|
-
if (
|
|
1495
|
+
const maybeConfig = findConfig({ dir: packageRoot });
|
|
1496
|
+
if (maybeConfig) {
|
|
1492
1497
|
return MicrofrontendsServer.fromFile({
|
|
1493
1498
|
filePath: maybeConfig,
|
|
1494
1499
|
cookies,
|
|
@@ -1503,12 +1508,15 @@ var MicrofrontendsServer = class extends Microfrontends {
|
|
|
1503
1508
|
repositoryRoot,
|
|
1504
1509
|
applicationName: packageJson.name
|
|
1505
1510
|
});
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1511
|
+
const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
|
|
1512
|
+
if (maybeConfigFromDefault) {
|
|
1513
|
+
return MicrofrontendsServer.fromFile({
|
|
1514
|
+
filePath: maybeConfigFromDefault,
|
|
1515
|
+
cookies,
|
|
1516
|
+
meta: configMeta,
|
|
1517
|
+
options
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1512
1520
|
}
|
|
1513
1521
|
throw new Error("Unable to infer");
|
|
1514
1522
|
} catch (e) {
|
|
@@ -1528,7 +1536,7 @@ var MicrofrontendsServer = class extends Microfrontends {
|
|
|
1528
1536
|
options
|
|
1529
1537
|
}) {
|
|
1530
1538
|
try {
|
|
1531
|
-
const configJson =
|
|
1539
|
+
const configJson = import_node_fs7.default.readFileSync(filePath, "utf-8");
|
|
1532
1540
|
const config = MicrofrontendsServer.validate(configJson);
|
|
1533
1541
|
if (!isMainConfig(config) && (options == null ? void 0 : options.resolveMainConfig)) {
|
|
1534
1542
|
const repositoryRoot = findRepositoryRoot();
|
|
@@ -1544,9 +1552,15 @@ var MicrofrontendsServer = class extends Microfrontends {
|
|
|
1544
1552
|
{ type: "config", subtype: "not_found" }
|
|
1545
1553
|
);
|
|
1546
1554
|
}
|
|
1547
|
-
const
|
|
1555
|
+
const maybeConfig = findConfig({ dir: packagePath });
|
|
1556
|
+
if (!maybeConfig) {
|
|
1557
|
+
throw new MicrofrontendError(
|
|
1558
|
+
`Could not find microfrontends configuration in ${packagePath}`,
|
|
1559
|
+
{ type: "config", subtype: "not_found" }
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1548
1562
|
return MicrofrontendsServer.fromMainConfigFile({
|
|
1549
|
-
filePath:
|
|
1563
|
+
filePath: maybeConfig,
|
|
1550
1564
|
overrides: cookies ? parseOverrides(cookies) : void 0
|
|
1551
1565
|
});
|
|
1552
1566
|
}
|
|
@@ -1570,7 +1584,7 @@ var MicrofrontendsServer = class extends Microfrontends {
|
|
|
1570
1584
|
overrides
|
|
1571
1585
|
}) {
|
|
1572
1586
|
try {
|
|
1573
|
-
const config =
|
|
1587
|
+
const config = import_node_fs7.default.readFileSync(filePath, "utf-8");
|
|
1574
1588
|
const validatedConfig = MicrofrontendsServer.validate(config);
|
|
1575
1589
|
if (!isMainConfig(validatedConfig)) {
|
|
1576
1590
|
throw new MicrofrontendError(
|
|
@@ -1848,6 +1862,16 @@ function transform4(args) {
|
|
|
1848
1862
|
const buildBeforeFiles = () => {
|
|
1849
1863
|
const rewrites = /* @__PURE__ */ new Map();
|
|
1850
1864
|
if (!app.isDefault()) {
|
|
1865
|
+
rewrites.set(`/${app.getAssetPrefix()}/_next/:path+`, {
|
|
1866
|
+
destination: {
|
|
1867
|
+
pathname: `/_next/:path+`
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
rewrites.set(`/${app.getAssetPrefix()}/.well-known/vercel/flags`, {
|
|
1871
|
+
destination: {
|
|
1872
|
+
pathname: `/.well-known/vercel/flags`
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1851
1875
|
if (process.env.VERCEL_MICROFRONTENDS_CONSOLIDATE_SPEED_INSIGHTS === "1") {
|
|
1852
1876
|
rewrites.set(`/${app.getAssetPrefix()}/_vercel/:path*`, {
|
|
1853
1877
|
destination: { pathname: "/_vercel/:path*" }
|
|
@@ -1916,9 +1940,27 @@ function transform4(args) {
|
|
|
1916
1940
|
}
|
|
1917
1941
|
|
|
1918
1942
|
// src/next-v2/config/transforms/server-actions.ts
|
|
1943
|
+
function debugRewrites2(allowedOrigins) {
|
|
1944
|
+
if (process.env.MFE_DEBUG === "true" && allowedOrigins) {
|
|
1945
|
+
const indent = " ".repeat(4);
|
|
1946
|
+
const header = "server actions allowed origins";
|
|
1947
|
+
const separator = "\u23AF".repeat(header.length);
|
|
1948
|
+
const maxSourceLength = Math.max(
|
|
1949
|
+
...allowedOrigins.map((key) => key.length)
|
|
1950
|
+
);
|
|
1951
|
+
const table = allowedOrigins.map((origin, idx) => {
|
|
1952
|
+
const paddedSource = origin.padEnd(maxSourceLength);
|
|
1953
|
+
return `${indent} ${idx + 1}. ${paddedSource}`;
|
|
1954
|
+
}).join("\n");
|
|
1955
|
+
console.log(`${indent}${header}
|
|
1956
|
+
${indent}${separator}
|
|
1957
|
+
${table}
|
|
1958
|
+
`);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1919
1961
|
var formatDomainForServerAction = (domain) => domain.replace(/https?:\/\//, "");
|
|
1920
1962
|
function transform5(args) {
|
|
1921
|
-
var _a;
|
|
1963
|
+
var _a, _b;
|
|
1922
1964
|
const { next, app, microfrontend } = args;
|
|
1923
1965
|
if (microfrontend instanceof MicrofrontendChildConfig) {
|
|
1924
1966
|
console.warn(
|
|
@@ -1944,11 +1986,14 @@ function transform5(args) {
|
|
|
1944
1986
|
...existingServerActionConfig,
|
|
1945
1987
|
allowedOrigins: Array.from(
|
|
1946
1988
|
/* @__PURE__ */ new Set([
|
|
1989
|
+
// existing
|
|
1947
1990
|
...(existingServerActionConfig == null ? void 0 : existingServerActionConfig.allowedOrigins) ?? [],
|
|
1991
|
+
// this deployments host
|
|
1992
|
+
...process.env.VERCEL_URL ? [formatDomainForServerAction(process.env.VERCEL_URL)] : [],
|
|
1993
|
+
// default application host
|
|
1994
|
+
formatDomainForServerAction(defaultApplication.production.toString()),
|
|
1995
|
+
// environment specific microfrontend hosts
|
|
1948
1996
|
...appsToAllow.flatMap((a) => [
|
|
1949
|
-
formatDomainForServerAction(
|
|
1950
|
-
defaultApplication.production.toString()
|
|
1951
|
-
),
|
|
1952
1997
|
formatDomainForServerAction(
|
|
1953
1998
|
getDomainForCurrentEnvironment(microfrontend, a.name)
|
|
1954
1999
|
)
|
|
@@ -1957,6 +2002,7 @@ function transform5(args) {
|
|
|
1957
2002
|
)
|
|
1958
2003
|
}
|
|
1959
2004
|
};
|
|
2005
|
+
debugRewrites2((_b = next.experimental.serverActions) == null ? void 0 : _b.allowedOrigins);
|
|
1960
2006
|
return {
|
|
1961
2007
|
next
|
|
1962
2008
|
};
|
|
@@ -2066,7 +2112,7 @@ function getApplicationContext(opts) {
|
|
|
2066
2112
|
return { name: opts.appName };
|
|
2067
2113
|
}
|
|
2068
2114
|
try {
|
|
2069
|
-
const packageJsonString =
|
|
2115
|
+
const packageJsonString = import_node_fs8.default.readFileSync("./package.json", "utf-8");
|
|
2070
2116
|
const packageJson = JSON.parse(packageJsonString);
|
|
2071
2117
|
if (!packageJson.name) {
|
|
2072
2118
|
throw new MicrofrontendError(
|