Skip to content

Commit

Permalink
refactor(plugin): simplify impl (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
noahziheng authored Sep 23, 2022
1 parent f71fa5a commit 8e33bed
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 103 deletions.
6 changes: 5 additions & 1 deletion src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { LoaderFactory, Manifest } from './loader';
import { Application, ApplicationInitOptions } from './types';
import Trigger from './trigger';
import ConfigurationHandler from './configuration';
import { Logger } from './logger';
import { Logger, LoggerType } from './logger';

export class ArtusApplication implements Application {
public manifest?: Manifest;
Expand Down Expand Up @@ -48,6 +48,10 @@ export class ArtusApplication implements Application {
return this.container.get(ConfigurationHandler);
}

get logger(): LoggerType {
return this.container.get(Logger);
}

loadDefaultClass() {
// load Artus default clazz
this.container.set({ id: Container, value: this.container });
Expand Down
4 changes: 2 additions & 2 deletions src/loader/impl/plugin_config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PLUGIN_CONFIG_PATTERN } from '../../constant';
import { ArtusPlugin } from '../../plugin';
import { getPackagePath } from '../../plugin/common';
import { PluginConfigItem } from '../../plugin/types';
import { isMatch } from '../../utils';
import { DefineLoader } from '../decorator';
Expand Down Expand Up @@ -29,7 +29,7 @@ class PluginConfigLoader extends ConfigLoader implements Loader {
);
}
const loaderState = item.loaderState as { baseDir: string };
pluginConfigItem.path = ArtusPlugin.getPath(pluginConfigItem.package, [loaderState?.baseDir]);
pluginConfigItem.path = getPackagePath(pluginConfigItem.package, [loaderState?.baseDir]);
delete pluginConfigItem.package;
configObj[pluginName] = pluginConfigItem;
}
Expand Down
57 changes: 0 additions & 57 deletions src/plugin/base.ts

This file was deleted.

11 changes: 9 additions & 2 deletions src/plugin/common.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Plugin } from './types';
import path from 'path';
import { PluginType } from './types';

// A utils function that toplogical sort plugins
export function topologicalSort(pluginInstanceMap: Map<string, Plugin>, pluginDepEdgeList: [string, string][]): string[] {
export function topologicalSort(pluginInstanceMap: Map<string, PluginType>, pluginDepEdgeList: [string, string][]): string[] {
const res: string[] = [];
const indegree: Map<string, number> = new Map();

Expand Down Expand Up @@ -31,3 +32,9 @@ export function topologicalSort(pluginInstanceMap: Map<string, Plugin>, pluginDe
}
return res;
}

// A util function of get package path for plugin
export function getPackagePath(packageName: string, paths?: string[]): string {
const opts = paths ? { paths } : undefined;
return path.resolve(require.resolve(packageName, opts), '..');
}
30 changes: 7 additions & 23 deletions src/plugin/factory.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { BasePlugin } from './base';
import { topologicalSort } from './common';
import { ArtusPlugin } from './impl';
import { PluginConfigItem } from './types';
import { Plugin } from './impl';
import { PluginConfigItem, PluginCreateOptions, PluginMap, PluginType } from './types';

export class PluginFactory {
static async create(name: string, item: PluginConfigItem): Promise<BasePlugin> {
const pluginInstance = new ArtusPlugin(name, item);
static async create(name: string, item: PluginConfigItem, opts?: PluginCreateOptions): Promise<PluginType> {
const pluginInstance = new Plugin(name, item, opts);
await pluginInstance.init();
return pluginInstance;
}

static async createFromConfig(config: Record<string, PluginConfigItem>): Promise<BasePlugin[]> {
const pluginInstanceMap: Map<string, BasePlugin> = new Map();
static async createFromConfig(config: Record<string, PluginConfigItem>, opts?: PluginCreateOptions): Promise<PluginType[]> {
const pluginInstanceMap: PluginMap = new Map();
for (const [name, item] of Object.entries(config)) {
const pluginInstance = await PluginFactory.create(name, item);
const pluginInstance = await PluginFactory.create(name, item, opts);
if (pluginInstance.enable) {
pluginInstanceMap.set(name, pluginInstance);
}
Expand All @@ -31,19 +30,4 @@ export class PluginFactory {
}
return pluginSortResult.map(name => pluginInstanceMap.get(name)!);
}

static filterDuplicatePlugins(plugins: BasePlugin[]): BasePlugin[] {
const exists: Map<string, boolean> = new Map();
const filtedPlugins: BasePlugin[] = [];
for (const plugin of plugins) {
const key = plugin.importPath;
if (exists.get(key)) {
continue;
}
exists.set(key, true);
filtedPlugins.push(plugin);
}

return filtedPlugins;
}
}
52 changes: 50 additions & 2 deletions src/plugin/impl.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
import path from 'path';
import { BasePlugin } from './base';
import { loadMetaFile } from '../utils/load_meta_file';
import { exisis } from '../utils/fs';
import { PLUGIN_META_FILENAME } from '../constant';
import { PluginConfigItem, PluginCreateOptions, PluginMap, PluginMetadata, PluginType } from './types';
import { getPackagePath } from './common';
import { LoggerType } from '../logger';

export class Plugin implements PluginType {
public name: string;
public enable: boolean;
public importPath = '';
public metadata: Partial<PluginMetadata> = {};
public metaFilePath = '';

private logger?: LoggerType;

constructor(name: string, configItem: PluginConfigItem, opts?: PluginCreateOptions) {
this.name = name;
this.enable = configItem.enable ?? false;
if (this.enable) {
let importPath = configItem.path ?? '';
if (configItem.package) {
if (importPath) {
throw new Error(`plugin ${name} config error, package and path can't be set at the same time.`);
}
importPath = getPackagePath(configItem.package);
}
if (!importPath) {
throw new Error(`Plugin ${name} need have path or package field`);
}
this.importPath = importPath;
}
this.logger = opts?.logger;
}

export class ArtusPlugin extends BasePlugin {
async init() {
if (!this.enable) {
return;
Expand All @@ -18,6 +47,25 @@ export class ArtusPlugin extends BasePlugin {
}
}

public checkDepExisted(pluginMap: PluginMap) {
for (const { name: pluginName, optional } of this.metadata.dependencies ?? []) {
const instance = pluginMap.get(pluginName);
if (!instance || !instance.enable) {
if (optional) {
this.logger?.warn(`Plugin ${this.name} need have optional dependence: ${pluginName}.`);
} else {
throw new Error(`Plugin ${this.name} need have dependence: ${pluginName}.`);
}
}
}
}

public getDepEdgeList(): [string, string][] {
return this.metadata.dependencies
?.filter(({ optional }) => !optional)
?.map(({ name: depPluginName }) => [this.name, depPluginName]) ?? [];
}

private async checkAndLoadMetadata() {
// check import path
if (!await exisis(this.importPath)) {
Expand Down
4 changes: 1 addition & 3 deletions src/plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {} from './types';

export * from './base';
export * from './types';
export * from './impl';
export * from './factory';
15 changes: 9 additions & 6 deletions src/plugin/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
export enum PluginType {
simple = 'simple',
module = 'module',
import { LoggerType } from '../logger';

export interface PluginCreateOptions {
logger?: LoggerType;
}

export interface PluginMetadata {
name: string;
dependencies?: PluginDependencyItem[];
type?: PluginType;
type?: 'simple' | 'module' | string;
configDir?: string
exclude?: string[];
}
Expand All @@ -22,14 +23,16 @@ export interface PluginConfigItem {
package?: string;
}

export interface Plugin {
export type PluginMap = Map<string, PluginType>;

export interface PluginType {
name: string;
enable: boolean;
importPath: string;
metadata: Partial<PluginMetadata>;
metaFilePath: string;

init(): Promise<void>;
checkDepExisted(map: Map<string, Plugin>): void;
checkDepExisted(map: PluginMap): void;
getDepEdgeList(): [string, string][];
}
8 changes: 5 additions & 3 deletions src/scanner/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { LoaderFactory, Manifest, ManifestItem } from '../loader';
import { ScannerOptions, WalkOptions } from './types';
import ConfigurationHandler, { ConfigObject } from '../configuration';
import { FrameworkConfig, FrameworkHandler } from '../framework';
import { BasePlugin, PluginFactory } from '../plugin';
import { PluginType, PluginFactory } from '../plugin';
import { ScanUtils } from './utils';
import { PluginConfigItem, PluginMetadata } from '../plugin/types';
import { getConfigMetaFromFilename } from '../loader/utils/config_file_meta';
Expand Down Expand Up @@ -116,7 +116,9 @@ export class Scanner {
}
const { plugin } = this.configHandle.getMergedConfig(env);
const pluginConfig = deepmerge.all([plugin || {}, this.options.plugin || {}]) as Record<string, PluginConfigItem>;
const pluginSortedList = await PluginFactory.createFromConfig(pluginConfig);
const pluginSortedList = await PluginFactory.createFromConfig(pluginConfig, {
logger: this.app.logger,
});
for (const plugin of pluginSortedList) {
if (!plugin.enable) continue;
this.setPluginMeta(plugin);
Expand Down Expand Up @@ -149,7 +151,7 @@ export class Scanner {
await ScanUtils.walk(root, options);
}

private setPluginMeta(plugin: BasePlugin) {
private setPluginMeta(plugin: PluginType) {
const metaList = this.itemMap.get('plugin-meta') ?? [];
metaList.push({
path: plugin.metaFilePath,
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Container } from '@artus/injection';
import { BaseContext } from '@artus/pipeline';
import { HookFunction } from './lifecycle';
import { Manifest } from './loader';
import { LoggerType } from './logger';

export interface ApplicationLifecycle {
configWillLoad?: HookFunction;
Expand All @@ -23,6 +24,9 @@ export interface Application {
manifest?: Manifest;
config?: Record<string, any>;

// getter
logger: LoggerType

load(manifest: Manifest): Promise<this>;
run(): Promise<void>;
registerHook(hookName: string, hookFn: HookFunction): void;
Expand Down
10 changes: 6 additions & 4 deletions test/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'reflect-metadata';
import path from 'path';
import { ArtusPlugin, PluginFactory } from '../src';
import { Logger, Plugin, PluginFactory } from '../src';

const pluginPrefix = 'fixtures/plugins';

Expand Down Expand Up @@ -45,7 +45,7 @@ describe('test/app.test.ts', () => {
const pluginList = await PluginFactory.createFromConfig(mockPluginConfig);
expect(pluginList.length).toEqual(3);
pluginList.forEach(plugin => {
expect(plugin).toBeInstanceOf(ArtusPlugin);
expect(plugin).toBeInstanceOf(Plugin);
expect(plugin.enable).toBeTruthy();
});
expect(pluginList.map(plugin => plugin.name)).toStrictEqual(['plugin-c', 'plugin-b', 'plugin-a']);
Expand Down Expand Up @@ -193,10 +193,12 @@ describe('test/app.test.ts', () => {
const originWarn = console.warn;
const mockWarnFn = jest.fn();
console.warn = mockWarnFn;
const pluginList = await PluginFactory.createFromConfig(mockPluginConfig);
const pluginList = await PluginFactory.createFromConfig(mockPluginConfig, {
logger: new Logger(),
});
expect(pluginList.length).toEqual(1);
pluginList.forEach(plugin => {
expect(plugin).toBeInstanceOf(ArtusPlugin);
expect(plugin).toBeInstanceOf(Plugin);
expect(plugin.enable).toBeTruthy();
});
expect(mockWarnFn).toBeCalledWith(`Plugin plugin-d need have optional dependence: plugin-e.`);
Expand Down

0 comments on commit 8e33bed

Please sign in to comment.