packages/@aws-cdk/toolkit-lib/lib/api/plugin/plugin.ts (56 lines of code) (raw):

import { inspect } from 'util'; import type { CredentialProviderSource, IPluginHost, Plugin } from '@aws-cdk/cli-plugin-contract'; import { type ContextProviderPlugin, isContextProviderPlugin } from './context-provider-plugin'; import type { IIoHost } from '../io'; import { IoDefaultMessages, IoHelper } from '../io/private'; import { ToolkitError } from '../toolkit-error'; export let TESTING = false; export function markTesting() { TESTING = true; } /** * Class to manage a plugin collection * * It provides a `load()` function that loads a JavaScript * module from disk, and gives it access to the `IPluginHost` interface * to register itself. */ export class PluginHost implements IPluginHost { /** * Access the currently registered CredentialProviderSources. New sources can * be registered using the +registerCredentialProviderSource+ method. */ public readonly credentialProviderSources = new Array<CredentialProviderSource>(); public readonly contextProviderPlugins: Record<string, ContextProviderPlugin> = {}; public ioHost?: IIoHost; private readonly alreadyLoaded = new Set<string>(); /** * Loads a plug-in into this PluginHost. * * Will use `require.resolve()` to get the most accurate representation of what * code will get loaded in error messages. As such, it will not work in * unit tests with Jest virtual modules becauase of <https://github.com/jestjs/jest/issues/9543>. * * @param moduleSpec the specification (path or name) of the plug-in module to be loaded. * @param ioHost the I/O host to use for printing progress information */ public load(moduleSpec: string, ioHost?: IIoHost) { try { const resolved = require.resolve(moduleSpec); if (ioHost) { new IoDefaultMessages(IoHelper.fromIoHost(ioHost, 'init')).debug(`Loading plug-in: ${resolved} from ${moduleSpec}`); } return this._doLoad(resolved); } catch (e: any) { // according to Node.js docs `MODULE_NOT_FOUND` is the only possible error here // @see https://nodejs.org/api/modules.html#requireresolverequest-options // Not using `withCause()` here, since the node error contains a "Require Stack" // as part of the error message that is inherently useless to our users. throw new ToolkitError(`Unable to resolve plug-in: Cannot find module '${moduleSpec}': ${e}`); } } /** * Do the loading given an already-resolved module name * * @internal */ public _doLoad(resolved: string) { try { if (this.alreadyLoaded.has(resolved)) { return; } /* eslint-disable @typescript-eslint/no-require-imports */ const plugin = require(resolved); /* eslint-enable */ if (!isPlugin(plugin)) { throw new ToolkitError(`Module ${resolved} is not a valid plug-in, or has an unsupported version.`); } if (plugin.init) { plugin.init(this); } this.alreadyLoaded.add(resolved); } catch (e: any) { throw ToolkitError.withCause(`Unable to load plug-in '${resolved}'`, e); } function isPlugin(x: any): x is Plugin { return x != null && x.version === '1'; } } /** * Allows plug-ins to register new CredentialProviderSources. * * @param source a new CredentialProviderSource to register. */ public registerCredentialProviderSource(source: CredentialProviderSource) { // Forward to the right credentials-related plugin host this.credentialProviderSources.push(source); } /** * (EXPERIMENTAL) Allow plugins to register context providers * * Context providers are objects with the following method: * * ```ts * getValue(args: {[key: string]: any}): Promise<any>; * ``` * * Currently, they cannot reuse the CDK's authentication mechanisms, so they * must be prepared to either not make AWS calls or use their own source of * AWS credentials. * * This feature is experimental, and only intended to be used internally at Amazon * as a trial. * * After registering with 'my-plugin-name', the provider must be addressed as follows: * * ```ts * const value = ContextProvider.getValue(this, { * providerName: 'plugin', * props: { * pluginName: 'my-plugin-name', * myParameter1: 'xyz', * }, * includeEnvironment: true | false, * dummyValue: 'what-to-return-on-the-first-pass', * }) * ``` * * @experimental */ public registerContextProviderAlpha(pluginProviderName: string, provider: ContextProviderPlugin) { if (!isContextProviderPlugin(provider)) { throw new ToolkitError(`Object you gave me does not look like a ContextProviderPlugin: ${inspect(provider)}`); } this.contextProviderPlugins[pluginProviderName] = provider; } }