108 lines · 3.1 KB · TypeScript
RawDownload
import {  createElement,  type ComponentType,} from "react";import type {  ExecutorFrontendPlugin,  FrontendPluginRouteDefinition,} from "./types";import {  normalizeExecutorPluginPath,} from "./paths"; export type RegisteredFrontendPluginRoute = {  plugin: ExecutorFrontendPlugin;  route: FrontendPluginRouteDefinition;}; export const defineFrontendPluginRoute = <  TDefinition extends FrontendPluginRouteDefinition,>(  definition: TDefinition,): TDefinition => definition; export const defineExecutorFrontendPlugin = <  TPlugin extends ExecutorFrontendPlugin,>(  input: TPlugin,): TPlugin => input; // Plugin objects are registered once at module load, so route components need// to resolve lazily for React Fast Refresh to see edits inside plugin packages.export const liveFrontendPluginComponent = <  TProps extends object,>(  resolveComponent: () => ComponentType<TProps>,): ComponentType<TProps> => {  const resolvedComponent = resolveComponent();   const LiveFrontendPluginComponent = (props: TProps) =>    createElement(resolveComponent(), props);   LiveFrontendPluginComponent.displayName =    `LiveFrontendPluginComponent(${resolvedComponent.displayName ?? resolvedComponent.name ?? "Anonymous"})`;   return LiveFrontendPluginComponent;}; export const registerExecutorFrontendPlugins = (  plugins: readonly ExecutorFrontendPlugin[],) => {  const pluginsByKey = new Map<string, ExecutorFrontendPlugin>();  const routesByPluginAndKey = new Map<string, RegisteredFrontendPluginRoute>();  const secretStoresByKind = new Map<string, ExecutorFrontendPlugin>();   for (const plugin of plugins) {    if (pluginsByKey.has(plugin.key)) {      throw new Error(`Duplicate frontend plugin registration: ${plugin.key}`);    }     pluginsByKey.set(plugin.key, plugin);     if (plugin.secretStore) {      if (secretStoresByKind.has(plugin.secretStore.kind)) {        throw new Error(          `Duplicate frontend secret store registration: ${plugin.secretStore.kind}`,        );      }       secretStoresByKind.set(plugin.secretStore.kind, plugin);    }     const pluginRouteKeys = new Set<string>();    const pluginRoutePaths = new Set<string>();     for (const route of plugin.routes ?? []) {      if (pluginRouteKeys.has(route.key)) {        throw new Error(          `Duplicate frontend plugin route key for ${plugin.key}: ${route.key}`,        );      }       pluginRouteKeys.add(route.key);       const normalizedPath = normalizeExecutorPluginPath(route.path ?? "");      if (pluginRoutePaths.has(normalizedPath)) {        throw new Error(          `Duplicate frontend plugin route path for ${plugin.key}: ${normalizedPath || "<root>"}`,        );      }       pluginRoutePaths.add(normalizedPath);      routesByPluginAndKey.set(`${plugin.key}:${route.key}`, {        plugin,        route,      });    }  }   const routes = [...routesByPluginAndKey.values()];   return {    plugins: [...pluginsByKey.values()],    routes,    getPlugin: (key: string) => pluginsByKey.get(key) ?? null,    getRoute: (pluginKey: string, routeKey: string) =>      routesByPluginAndKey.get(`${pluginKey}:${routeKey}`) ?? null,  };};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t