@vercel/microfrontends
Version:
Defines configuration and utilities for microfrontends development
1 lines • 8.8 kB
Source Map (JSON)
{"version":3,"sources":["../../src/next/endpoints/index.ts","../../src/config/microfrontends-config/client/index.ts","../../src/config/well-known/endpoints.ts"],"sourcesContent":["import type { NextApiResponse } from 'next';\nimport { NextResponse } from 'next/server';\nimport { getWellKnownClientData } from '../../config/well-known/endpoints';\nimport type { WellKnownClientData } from '../../config/well-known/types';\n\n/**\n * A Next.js App Router API handler to export the microfrontends client config\n * data.\n *\n * @example In the `app/.well-known/vercel/microfrontends/client-config/route.ts` file,\n * add this code:\n * ```\n * export { wellKnownNextjsClientConfigAppRoute as GET } from '@vercel/microfrontends/next/endpoints';\n * ```\n */\nexport async function wellKnownNextjsClientConfigAppRoute(\n flagValues: Record<string, () => Promise<boolean>>,\n): Promise<NextResponse> {\n return NextResponse.json<WellKnownClientData>(\n await getWellKnownClientData(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n flagValues,\n ),\n );\n}\n\n/**\n * A Next.js Pages Router API to export the microfrontends client config data to the client.\n *\n * @example In the `pages/api/.well-known/vercel/microfrontends/client-config/index.ts` file,\n * add this code:\n * ```\n * import { handleClientConfigForPagesRouter } from '@vercel/microfrontends/next/endpoints';\n * import type { NextApiResponse } from 'next';\n *\n * async function handler(\n * _,\n * res: NextApiResponse,\n * ): Promise<void> {\n * await handleClientConfigForPagesRouter(res, {\n * flagValue: () => Promise.resolve(true)\n * });\n * }\n * ```\n *\n * Then also make sure to add the following rewrite rule to your `next.config.js` file:\n * ```\n * rewrites: () =>\n * Promise.resolve([\n * {\n * source: '/.well-known/vercel/microfrontends/client-config',\n * destination: '/api/.well-known/vercel/microfrontends/client-config',\n * },\n * ]),\n * ```\n */\nexport async function handleClientConfigForPagesRouter(\n res: NextApiResponse,\n flagValues: Record<string, () => Promise<boolean>>,\n): Promise<void> {\n const clientData = await getWellKnownClientData(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n flagValues,\n );\n res.status(200).json(clientData);\n}\n\nexport { getWellKnownClientData };\nexport type { WellKnownClientData };\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 { MicrofrontendConfigClient } from '../microfrontends-config/client';\nimport type { WellKnownClientData } from './types';\n\n/**\n * Returns data used by the client to ensure that navigations across\n * microfrontend boundaries are routed and prefetched correctly. The client\n * configuration is safe to expose to users.\n *\n * This data should be exposed in a `.well-known/vercel/microfrontends/client-config`\n * endpoint.\n */\nexport async function getWellKnownClientData(\n clientConfig: string | undefined,\n flagValues: Record<string, () => Promise<boolean>> = {},\n): Promise<WellKnownClientData> {\n const config = MicrofrontendConfigClient.fromEnv(clientConfig);\n for (const [applicationName, application] of Object.entries(\n config.applications,\n )) {\n if (!application.routing) {\n continue;\n }\n const newRoutingMatches = [];\n for (const pathGroup of application.routing) {\n if (pathGroup.flag) {\n const flagName = pathGroup.flag;\n const flagFn = flagValues[flagName];\n if (!flagFn) {\n throw new Error(\n `Flag \"${flagName}\" was specified to control routing for path group \"${pathGroup.group}\" in application ${applicationName} but not found in provided flag values.`,\n );\n }\n // eslint-disable-next-line no-await-in-loop\n const flagEnabled = await flagFn();\n if (flagEnabled) {\n newRoutingMatches.push(pathGroup);\n }\n } else {\n newRoutingMatches.push(pathGroup);\n }\n }\n application.routing = newRoutingMatches;\n }\n\n return {\n config: config.serialize(),\n };\n}\n"],"mappings":"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","names":[]}