Repository navigation
Expand file tree
/
Copy pathHeftPluginHost.ts
More file actions
99 lines (89 loc) · 3.85 KB
/
Copy pathHeftPluginHost.ts
File metadata and controls
99 lines (89 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import { SyncHook } from 'tapable';
import { InternalError } from '@rushstack/node-core-library';
import type { ITerminal } from '@rushstack/terminal';
import type { HeftPluginDefinitionBase } from '../configuration/HeftPluginDefinition';
import type { IHeftPlugin } from './IHeftPlugin';
export abstract class HeftPluginHost {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly #pluginAccessRequestHooks: Map<string, SyncHook<any>> = new Map();
#pluginsApplied: boolean = false;
public async applyPluginsAsync(terminal: ITerminal): Promise<void> {
if (this.#pluginsApplied) {
// No need to apply them a second time.
return;
}
terminal.writeVerboseLine('Applying plugins');
await this.applyPluginsInternalAsync();
this.#pluginsApplied = true;
}
protected abstract applyPluginsInternalAsync(): Promise<void>;
/**
* Registers a callback used to provide access to a requested plugin via the plugin accessor.
*/
public requestAccessToPluginByName<T extends object>(
requestorName: string,
pluginToAccessPackage: string,
pluginToAccessName: string,
accessorCallback: (pluginAccessor: T) => void
): void {
if (this.#pluginsApplied) {
throw new Error(
`Requestor ${JSON.stringify(requestorName)} cannot request access to plugin ` +
`${JSON.stringify(pluginToAccessName)} from package ${JSON.stringify(pluginToAccessPackage)} ` +
`after plugins have been applied.`
);
}
const pluginHookName: string = this.getPluginHookName(pluginToAccessPackage, pluginToAccessName);
let pluginAccessRequestHook: SyncHook<T> | undefined = this.#pluginAccessRequestHooks.get(pluginHookName);
if (!pluginAccessRequestHook) {
pluginAccessRequestHook = new SyncHook(['pluginAccessor']);
this.#pluginAccessRequestHooks.set(pluginHookName, pluginAccessRequestHook);
}
if (pluginAccessRequestHook.taps.some((t) => t.name === requestorName)) {
throw new Error(
`Plugin ${JSON.stringify(pluginToAccessName)} from ${JSON.stringify(pluginToAccessPackage)} has ` +
`already been accessed by ${JSON.stringify(requestorName)}.`
);
}
pluginAccessRequestHook.tap(requestorName, accessorCallback);
}
/**
* Gets the name of the hook that is registered with the plugin access request hook.
*/
public getPluginHookName(pluginPackageName: string, pluginName: string): string {
return `${pluginPackageName};${pluginName}`;
}
/**
* Resolves all plugin requests for the specified plugin. All plugins that requested access to the
* specified plugin will have their callbacks invoked, and will be provided with the accessor for
* the specified plugin.
*/
protected resolvePluginAccessRequests(
plugin: IHeftPlugin,
pluginDefinition: HeftPluginDefinitionBase
): void {
if (this.#pluginsApplied) {
throw new InternalError('Cannot resolve plugin access requests after plugins have been applied.');
}
const pluginHookName: string = this.getPluginHookName(
pluginDefinition.pluginPackageName,
pluginDefinition.pluginName
);
const pluginAccessRequestHook: SyncHook<object> | undefined =
this.#pluginAccessRequestHooks.get(pluginHookName);
if (pluginAccessRequestHook?.isUsed()) {
const accessor: object | undefined = plugin.accessor;
if (accessor) {
pluginAccessRequestHook.call(accessor);
} else {
throw new Error(
`Plugin ${JSON.stringify(pluginDefinition.pluginName)} from package ` +
`${JSON.stringify(pluginDefinition.pluginPackageName)} does not provide an accessor property, ` +
`so it does not provide access to other plugins.`
);
}
}
}
}