@vercel/microfrontends
Version:
Defines configuration and utilities for microfrontends development
125 lines (122 loc) • 3.71 kB
JavaScript
// src/next/endpoints/index.ts
import { NextResponse } from "next/server";
// src/config/microfrontends-config/client/index.ts
import { pathToRegexp } from "path-to-regexp";
var MicrofrontendConfigClient = class {
constructor(config, opts) {
this.pathCache = {};
this.serialized = config;
if (opts?.removeFlaggedPaths) {
for (const app of Object.values(config.applications)) {
if (app.routing) {
app.routing = app.routing.filter((match) => !match.flag);
}
}
}
this.applications = config.applications;
}
/**
* Create a new `MicrofrontendConfigClient` from a JSON string.
* Config must be passed in to remain framework agnostic
*/
static fromEnv(config, opts) {
if (!config) {
throw new Error("No microfrontends configuration found");
}
return new MicrofrontendConfigClient(
JSON.parse(config),
opts
);
}
isEqual(other) {
return JSON.stringify(this.applications) === JSON.stringify(other.applications);
}
getApplicationNameForPath(path) {
if (!path.startsWith("/")) {
throw new Error(`Path must start with a /`);
}
if (this.pathCache[path]) {
return this.pathCache[path];
}
const pathname = new URL(path, "https://example.com").pathname;
for (const [name, application] of Object.entries(this.applications)) {
if (application.routing) {
for (const group of application.routing) {
for (const childPath of group.paths) {
const regexp = pathToRegexp(childPath);
if (regexp.test(pathname)) {
this.pathCache[path] = name;
return name;
}
}
}
}
}
const defaultApplication = Object.entries(this.applications).find(
([, application]) => application.default
);
if (!defaultApplication) {
return null;
}
this.pathCache[path] = defaultApplication[0];
return defaultApplication[0];
}
serialize() {
return this.serialized;
}
};
// src/config/well-known/endpoints.ts
async function getWellKnownClientData(clientConfig, flagValues = {}) {
const config = MicrofrontendConfigClient.fromEnv(clientConfig);
for (const [applicationName, application] of Object.entries(
config.applications
)) {
if (!application.routing) {
continue;
}
const newRoutingMatches = [];
for (const pathGroup of application.routing) {
if (pathGroup.flag) {
const flagName = pathGroup.flag;
const flagFn = flagValues[flagName];
if (!flagFn) {
throw new Error(
`Flag "${flagName}" was specified to control routing for path group "${pathGroup.group}" in application ${applicationName} but not found in provided flag values.`
);
}
const flagEnabled = await flagFn();
if (flagEnabled) {
newRoutingMatches.push(pathGroup);
}
} else {
newRoutingMatches.push(pathGroup);
}
}
application.routing = newRoutingMatches;
}
return {
config: config.serialize()
};
}
// src/next/endpoints/index.ts
async function wellKnownNextjsClientConfigAppRoute(flagValues) {
return NextResponse.json(
await getWellKnownClientData(
process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,
flagValues
)
);
}
async function handleClientConfigForPagesRouter(res, flagValues) {
const clientData = await getWellKnownClientData(
process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,
flagValues
);
res.status(200).json(clientData);
}
export {
getWellKnownClientData,
handleClientConfigForPagesRouter,
wellKnownNextjsClientConfigAppRoute
};
//# sourceMappingURL=endpoints.js.map