Skip to content

Commit b232f82

Browse files
Merge pull request #1426 from opencomponents/plugins-with-context
plugins with context
2 parents 60d0318 + 78160c7 commit b232f82

File tree

16 files changed

+196
-57
lines changed

16 files changed

+196
-57
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "oc",
3-
"version": "0.50.18",
3+
"version": "0.50.19",
44
"description": "A framework for developing and distributing html components",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

src/cli/domain/get-mocked-plugins.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import type { Logger } from '../logger';
99
interface MockedPlugin {
1010
register: (options: unknown, dependencies: unknown, next: () => void) => void;
1111
execute: (...args: unknown[]) => unknown;
12+
context?: boolean;
1213
}
1314

1415
interface PluginMock {
16+
context: boolean;
1517
name: string;
1618
register: {
1719
register: (
@@ -52,6 +54,7 @@ const registerStaticMocks = (
5254
logger.ok(`├── ${pluginName} () => ${mockedValue}`);
5355

5456
return {
57+
context: false,
5558
name: pluginName,
5659
register: {
5760
register: defaultRegister,
@@ -83,12 +86,14 @@ const registerDynamicMocks = (
8386

8487
const register = (pluginMock as MockedPlugin).register || defaultRegister;
8588
const execute = (pluginMock as MockedPlugin).execute || pluginMock;
89+
const context = (pluginMock as MockedPlugin).context || false;
8690

8791
logger.ok(`├── ${pluginName} () => [Function]`);
8892

8993
return {
9094
name: pluginName,
91-
register: { execute, register }
95+
register: { execute, register },
96+
context
9297
};
9398
})
9499
.filter((pluginMock): pluginMock is PluginMock => !!pluginMock);

src/cli/facade/dev.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const dev = ({ local, logger }: { logger: Logger; local: Local }) =>
113113
const registerPlugins = (registry: Registry) => {
114114
const mockedPlugins = getMockedPlugins(logger, componentsDir);
115115
for (const plugin of mockedPlugins) {
116-
registry.register(plugin);
116+
registry.register(plugin as any);
117117
}
118118

119119
registry.on('request', (data) => {

src/components/oc-client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "oc-client",
33
"description": "The OpenComponents client-side javascript client",
4-
"version": "0.50.18",
4+
"version": "0.50.19",
55
"repository": "https://github.com/opencomponents/oc/tree/master/components/oc-client",
66
"author": "Matteo Figus <[email protected]>",
77
"oc": {

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export { default as Client } from 'oc-client';
22
export { default as cli } from './cli/programmatic-api';
33
export { default as Registry, RegistryOptions } from './registry';
4+
5+
export type { Plugin, PluginContext } from './types';

src/registry/domain/options-sanitiser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type CompileOptions = Omit<
1212
>;
1313

1414
export interface RegistryOptions<T = any>
15-
extends Partial<Omit<Config<T>, 'beforePublish' | 'discovery'>> {
15+
extends Partial<Omit<Config<T>, 'beforePublish' | 'discovery' | 'plugins'>> {
1616
/**
1717
* Configuration object to enable/disable the HTML discovery page and the API
1818
*

src/registry/domain/plugins-initialiser.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { promisify } from 'node:util';
22
import { DepGraph } from 'dependency-graph';
33
import strings from '../../resources';
4-
import type { Plugin } from '../../types';
4+
import type { Plugin, Plugins } from '../../types';
55
import pLimit from '../../utils/pLimit';
66

7+
type PluginWithCallback = Plugin & {
8+
callback?: (error?: unknown) => void;
9+
};
10+
711
function validatePlugins(plugins: unknown[]): asserts plugins is Plugin[] {
812
for (let idx = 0; idx < plugins.length; idx++) {
913
const plugin = plugins[idx] as Plugin;
@@ -46,12 +50,8 @@ function checkDependencies(plugins: Plugin[]) {
4650

4751
let deferredLoads: Plugin[] = [];
4852

49-
type PluginFunctions = Record<string, (...args: unknown[]) => void>;
50-
51-
export async function init(
52-
pluginsToRegister: unknown[]
53-
): Promise<PluginFunctions> {
54-
const registered: PluginFunctions = {};
53+
export async function init(pluginsToRegister: unknown[]): Promise<Plugins> {
54+
const registered: Plugins = {};
5555

5656
validatePlugins(pluginsToRegister);
5757
checkDependencies(pluginsToRegister);
@@ -71,7 +71,7 @@ export async function init(
7171
return present;
7272
};
7373

74-
const loadPlugin = async (plugin: Plugin): Promise<void> => {
74+
const loadPlugin = async (plugin: PluginWithCallback): Promise<void> => {
7575
if (registered[plugin.name]) {
7676
return;
7777
}
@@ -98,14 +98,15 @@ export async function init(
9898
pluginCallback(err);
9999
throw err;
100100
});
101-
// Overriding toString so implementation details of plugins do not
102-
// leak to OC consumers
103-
plugin.register.execute.toString = () => plugin.description || '';
104-
registered[plugin.name] = plugin.register.execute;
101+
registered[plugin.name] = {
102+
handler: plugin.register.execute as any,
103+
description: plugin.description || '',
104+
context: plugin.context || false
105+
};
105106
pluginCallback();
106107
};
107108

108-
const terminator = async (): Promise<PluginFunctions> => {
109+
const terminator = async (): Promise<Plugins> => {
109110
if (deferredLoads.length > 0) {
110111
const deferredPlugins = [...deferredLoads];
111112
deferredLoads = [];

src/registry/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default function registry<T = any>(inputOptions: RegistryOptions<T>) {
3535
};
3636

3737
const register = <T = any>(
38-
plugin: Omit<Plugin<T>, 'callback'>,
38+
plugin: Plugin<T>,
3939
callback?: (...args: any[]) => void
4040
) => {
4141
plugins.push(Object.assign(plugin, { callback }));
@@ -48,10 +48,10 @@ export default function registry<T = any>(inputOptions: RegistryOptions<T>) {
4848
) => void
4949
) => {
5050
const ok = (msg: string) => console.log(colors.green(msg));
51-
createRouter(app, options, repository);
5251

5352
try {
5453
options.plugins = await pluginsInitialiser.init(plugins);
54+
createRouter(app, options, repository);
5555
const componentsInfo = await repository.init();
5656
await appStart(repository, options);
5757

src/registry/routes/helpers/get-component.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ import emptyResponseHandler from 'oc-empty-response-handler';
99
import { fromPromise } from 'universalify';
1010
import strings from '../../../resources';
1111
import settings from '../../../resources/settings';
12-
import type { Component, Config, Template } from '../../../types';
12+
import type {
13+
Component,
14+
Config,
15+
PluginContext,
16+
Plugins,
17+
Template
18+
} from '../../../types';
1319
import isTemplateLegacy from '../../../utils/is-template-legacy';
1420
import eventsHandler from '../../domain/events-handler';
1521
import NestedRenderer from '../../domain/nested-renderer';
@@ -62,12 +68,57 @@ const noopConsole = Object.fromEntries(
6268
Object.keys(console).map((key) => [key, noop])
6369
);
6470

71+
/**
72+
* Converts the plugins to a function that returns a record of plugins with the context applied
73+
* Caches the plugins without context and applies the context to the plugins that need it
74+
* to avoid creating a new function for each component if possible
75+
* @param plugins - The plugins to convert
76+
* @returns A function that returns a record of plugins with the context applied
77+
*/
78+
function pluginConverter(plugins: Plugins = {}) {
79+
const pluginsMap = {
80+
withoutContext: {} as Record<string, (...args: any[]) => any>,
81+
withContext: {} as Record<
82+
string,
83+
(ctx: PluginContext) => (...args: any[]) => any
84+
>,
85+
needsContext: false
86+
};
87+
for (const [name, { handler, context }] of Object.entries(plugins)) {
88+
if (context) {
89+
pluginsMap.withContext[name] = handler as any;
90+
pluginsMap.needsContext = true;
91+
} else {
92+
pluginsMap.withoutContext[name] = handler;
93+
}
94+
}
95+
96+
return (ctx: PluginContext) => {
97+
if (!pluginsMap.needsContext) {
98+
return pluginsMap.withoutContext;
99+
}
100+
const pluginsWithContextApplied = {} as Record<
101+
string,
102+
(...args: any[]) => any
103+
>;
104+
for (const [name, handler] of Object.entries(pluginsMap.withContext)) {
105+
pluginsWithContextApplied[name] = handler(ctx);
106+
}
107+
108+
return {
109+
...pluginsMap.withoutContext,
110+
...pluginsWithContextApplied
111+
};
112+
};
113+
}
114+
65115
export default function getComponent(conf: Config, repository: Repository) {
66116
const client = Client({ templates: conf.templates });
67117
const cache = new Cache({
68118
verbose: !!conf.verbosity,
69119
refreshInterval: conf.refreshInterval
70120
});
121+
const convertPlugins = pluginConverter(conf.plugins);
71122

72123
const getEnv = async (
73124
component: Component
@@ -504,7 +555,10 @@ export default function getComponent(conf: Config, repository: Repository) {
504555
baseUrl: conf.baseUrl,
505556
env: { ...conf.env, ...env },
506557
params,
507-
plugins: conf.plugins,
558+
plugins: convertPlugins({
559+
name: component.name,
560+
version: component.version
561+
}),
508562
renderComponent: fromPromise(nestedRenderer.renderComponent),
509563
renderComponents: fromPromise(nestedRenderer.renderComponents),
510564
requestHeaders: options.headers,

src/registry/routes/plugins.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ export default function plugins(conf: Config) {
55
return (_req: Request, res: Response): void => {
66
if (res.conf.discovery.ui) {
77
const plugins = Object.entries(conf.plugins).map(
8-
([pluginName, pluginFn]) => ({
8+
([pluginName, plugin]) => ({
99
name: pluginName,
10-
description: pluginFn.toString()
10+
description: plugin.description
1111
})
1212
);
1313

0 commit comments

Comments
 (0)