mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-12 17:08:14 +00:00
feat: 支持新版的 plugin 机制
fix: 兼容 vision 版本 select / radiogroup setter chore: 优化工程化
This commit is contained in:
parent
55a9e26d1a
commit
1e8fc63321
@ -29,6 +29,7 @@
|
||||
"**/*.md",
|
||||
"**/test/**"
|
||||
],
|
||||
"message": "chore(release): publish %v",
|
||||
"conventionalCommits": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,9 +14,9 @@
|
||||
"clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build",
|
||||
"lint": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet",
|
||||
"lint:fix": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet --fix",
|
||||
"pub": "lerna publish --force-publish --cd-version patch --message \"chore(release): publish %v\"",
|
||||
"pub:prepatch": "lerna publish --force-publish --cd-version prepatch --npm-tag beta --preid beta --message \"chore(release): publish %v\"",
|
||||
"pub:prerelease": "lerna publish --force-publish --cd-version prerelease --npm-tag beta --preid beta --message \"chore(release): publish %v\"",
|
||||
"pub": "lerna publish --force-publish --cd-version patch",
|
||||
"pub:prepatch": "lerna publish --force-publish --cd-version prepatch --npm-tag beta --preid beta",
|
||||
"pub:prerelease": "lerna publish --force-publish --cd-version prerelease --npm-tag beta --preid beta",
|
||||
"setup": "./scripts/setup.sh",
|
||||
"start": "./scripts/start.sh",
|
||||
"start:server": "./scripts/start-server.sh",
|
||||
|
||||
@ -14,6 +14,8 @@ module.exports = {
|
||||
'no-useless-constructor': 1,
|
||||
'no-empty-function': 1,
|
||||
'@typescript-eslint/member-ordering': 0,
|
||||
'lines-between-class-members': 0
|
||||
'lines-between-class-members': 0,
|
||||
'no-await-in-loop': 0,
|
||||
'no-plusplus': 0,
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,8 @@
|
||||
"enzyme-adapter-react-16": "^1.15.5",
|
||||
"event": "^1.0.0",
|
||||
"react": "^16",
|
||||
"react-dom": "^16.7.0"
|
||||
"react-dom": "^16.7.0",
|
||||
"zen-logger": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ali/lowcode-test-mate": "^1.0.1",
|
||||
|
||||
@ -4,3 +4,4 @@ export * from './designer';
|
||||
export * from './document';
|
||||
export * from './project';
|
||||
export * from './builtin-simulator';
|
||||
export * from './plugin';
|
||||
|
||||
3
packages/designer/src/plugin/index.ts
Normal file
3
packages/designer/src/plugin/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './plugin-context';
|
||||
export * from './plugin-manager';
|
||||
export * from './plugin';
|
||||
62
packages/designer/src/plugin/plugin-context.ts
Normal file
62
packages/designer/src/plugin/plugin-context.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { Editor, Hotkey, hotkey } from '@ali/lowcode-editor-core';
|
||||
import { Skeleton } from '@ali/lowcode-editor-skeleton';
|
||||
import { ComponentAction, ILowCodePluginConfig } from '@ali/lowcode-types';
|
||||
import { getLogger, Logger } from '../utils';
|
||||
import {
|
||||
registerMetadataTransducer,
|
||||
addBuiltinComponentAction,
|
||||
removeBuiltinComponentAction,
|
||||
MetadataTransducer,
|
||||
} from '../component-meta';
|
||||
import { Designer } from '../designer';
|
||||
|
||||
export interface IDesignerHelper {
|
||||
registerMetadataTransducer: (transducer: MetadataTransducer, level = 100, id?: string) => void;
|
||||
addBuiltinComponentAction: (action: ComponentAction) => void;
|
||||
removeBuiltinComponentAction: (actionName: string) => void;
|
||||
}
|
||||
|
||||
export interface ILowCodePluginContext {
|
||||
skeleton: Skeleton;
|
||||
designer: Designer;
|
||||
editor: Editor;
|
||||
hotkey: Hotkey;
|
||||
logger: Logger;
|
||||
plugins: LowCodePluginManager;
|
||||
designerHelper: IDesignerHelper;
|
||||
/**
|
||||
其他暂不增加,按需增加
|
||||
*/
|
||||
}
|
||||
|
||||
export default class PluginContext implements ILowCodePluginContext {
|
||||
editor: Editor;
|
||||
skeleton: Skeleton;
|
||||
designer: Designer;
|
||||
hotkey: Hotkey;
|
||||
logger: Logger;
|
||||
plugins: LowCodePluginManager;
|
||||
designerHelper: IDesignerHelper;
|
||||
|
||||
constructor(editor: Editor, plugins: LowCodePluginManager) {
|
||||
this.editor = editor;
|
||||
this.designer = editor.get('designer');
|
||||
this.skeleton = editor.get('skeleton');
|
||||
this.hotkey = hotkey;
|
||||
this.plugins = plugins;
|
||||
this.designerHelper = this.createDesignerHelper();
|
||||
}
|
||||
|
||||
private createDesignerHelper(): () => IDesignerHelper {
|
||||
return {
|
||||
registerMetadataTransducer,
|
||||
addBuiltinComponentAction,
|
||||
removeBuiltinComponentAction,
|
||||
};
|
||||
}
|
||||
|
||||
setLogger(config: ILowCodePluginConfig): (config: ILowCodePluginConfig) => void {
|
||||
this.logger = getLogger({ level: 'log', bizName: `designer:plugin:${config.name}` });
|
||||
}
|
||||
}
|
||||
|
||||
113
packages/designer/src/plugin/plugin-manager.ts
Normal file
113
packages/designer/src/plugin/plugin-manager.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import { CompositeObject, ILowCodePlugin, ILowCodePluginConfig, ILowCodePluginManager } from '@ali/lowcode-types';
|
||||
import { LowCodePlugin } from './plugin';
|
||||
import LowCodePluginContext from './plugin-context';
|
||||
import { getLogger, invariant } from '../utils';
|
||||
import sequencify from './sequencify';
|
||||
|
||||
const logger = getLogger({ level: 'warn', bizName: 'designer:pluginManager' });
|
||||
|
||||
export class LowCodePluginManager implements ILowCodePluginManager {
|
||||
private plugins: ILowCodePlugin[] = [];
|
||||
|
||||
private pluginsMap: Map<string, ILowCodePlugin> = new Map();
|
||||
|
||||
private editor: Editor;
|
||||
|
||||
constructor(editor: Editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
private _getLowCodePluginContext() {
|
||||
return new LowCodePluginContext(this.editor, this);
|
||||
}
|
||||
|
||||
register(
|
||||
pluginConfig: (ctx: ILowCodePluginContext, options: CompositeObject) => ILowCodePluginConfig,
|
||||
options: CompositeObject,
|
||||
): void {
|
||||
const ctx = this._getLowCodePluginContext();
|
||||
const config = pluginConfig(ctx, options);
|
||||
invariant(config.name, `${config.name} required`, config);
|
||||
ctx.setLogger(config);
|
||||
invariant(!this.pluginsMap.has(config.name), `${config.name} already exists`, this.pluginsMap.get(config.name));
|
||||
const plugin = new LowCodePlugin(this, config, options);
|
||||
this.plugins.push(plugin);
|
||||
this.pluginsMap.set(plugin.name, plugin);
|
||||
logger.log('plugin registered with config:', config, ', options:', options);
|
||||
}
|
||||
|
||||
get(pluginName: string): ILowCodePlugin {
|
||||
return this.pluginsMap.get(pluginName);
|
||||
}
|
||||
|
||||
getAll(): ILowCodePlugin[] {
|
||||
return this.plugins;
|
||||
}
|
||||
|
||||
has(pluginName: string): boolean {
|
||||
return this.pluginsMap.has(pluginName);
|
||||
}
|
||||
|
||||
async delete(pluginName: string): boolean {
|
||||
const idx = this.plugins.findIndex(plugin => plugin.name === pluginName);
|
||||
if (idx < -1) return;
|
||||
const plugin = this.plugins[idx];
|
||||
await plugin.destroy();
|
||||
|
||||
this.plugins.splice(idx, 1);
|
||||
return this.pluginsMap.delete(pluginName);
|
||||
}
|
||||
|
||||
async init() {
|
||||
const pluginNames = [];
|
||||
const pluginObj = {};
|
||||
this.plugins.forEach(plugin => {
|
||||
pluginNames.push(plugin.name);
|
||||
pluginObj[plugin.name] = plugin;
|
||||
});
|
||||
const { missingTasks, sequence } = sequencify(pluginObj, pluginNames);
|
||||
invariant(!missingTasks.length, 'plugin dependency missing', missingTasks);
|
||||
logger.log('load plugin sequence:', sequence);
|
||||
|
||||
for (const pluginName of sequence) {
|
||||
await this.pluginsMap.get(pluginName).init();
|
||||
}
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
for (const plugin of this.plugins) {
|
||||
await plugin.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.pluginsMap.size;
|
||||
}
|
||||
|
||||
toProxy() {
|
||||
return new Proxy(this, {
|
||||
get(target, prop, receiver) {
|
||||
if (target.pluginsMap.has(prop)) {
|
||||
// 禁用态的插件,直接返回 undefined
|
||||
if (target.pluginsMap.get(prop).disabled) {
|
||||
return undefined;
|
||||
}
|
||||
return target.pluginsMap.get(prop)?.toProxy();
|
||||
}
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setDisabled(pluginName: string, flag = true) {
|
||||
logger.warn(`plugin:${pluginName} has been set disable:${flag}`);
|
||||
this.pluginsMap.get(pluginName)?.setDisabled(flag);
|
||||
}
|
||||
|
||||
async dispose() {
|
||||
await this.destroy();
|
||||
this.plugins = [];
|
||||
this.pluginsMap.clear();
|
||||
}
|
||||
}
|
||||
91
packages/designer/src/plugin/plugin.ts
Normal file
91
packages/designer/src/plugin/plugin.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import {
|
||||
ILowCodePlugin,
|
||||
ILowCodePluginConfig,
|
||||
ILowCodePluginManager,
|
||||
CompositeObject,
|
||||
} from '@ali/lowcode-types';
|
||||
import { EventEmitter } from 'events';
|
||||
import { getLogger, Logger, invariant } from '../utils';
|
||||
|
||||
export class LowCodePlugin implements ILowCodePlugin {
|
||||
config: ILowCodePluginConfig;
|
||||
|
||||
logger: Logger;
|
||||
|
||||
private manager: ILowCodePluginManager;
|
||||
|
||||
private options?: CompositeObject;
|
||||
|
||||
private emiter: EventEmitter;
|
||||
|
||||
private _inited: boolean;
|
||||
|
||||
/**
|
||||
* 标识插件状态,是否被 disabled
|
||||
*/
|
||||
private _disabled: boolean;
|
||||
|
||||
constructor(
|
||||
manager: ILowCodePluginManager,
|
||||
config: ILowCodePluginConfig = {},
|
||||
options: CompositeObject = {},
|
||||
) {
|
||||
this.manager = manager;
|
||||
this.config = config;
|
||||
this.options = options;
|
||||
this.emiter = new EventEmitter();
|
||||
this.logger = getLogger({ level: 'log', bizName: `designer:plugin:${config.name}` });
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.config.name;
|
||||
}
|
||||
|
||||
get dep() {
|
||||
return this.config.dep || [];
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
on(...args) {
|
||||
return this.emiter.on(...args);
|
||||
}
|
||||
|
||||
emit(...args) {
|
||||
return this.emiter.emit(...args);
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.logger.log('method init called');
|
||||
await this.config.init?.call();
|
||||
this._inited = true;
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
this.logger.log('method destroy called');
|
||||
await this.config.destroy?.call();
|
||||
}
|
||||
|
||||
private setDisabled(flag = true) {
|
||||
this._disabled = flag;
|
||||
}
|
||||
|
||||
toProxy() {
|
||||
invariant(this._inited, 'Could not call toProxy before init');
|
||||
const exports = this.config.exports?.();
|
||||
return new Proxy(this, {
|
||||
get(target, prop, receiver) {
|
||||
if (hasOwnProperty.call(exports, prop)) {
|
||||
return exports[prop];
|
||||
}
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
return this.manager.delete(this.name);
|
||||
}
|
||||
}
|
||||
40
packages/designer/src/plugin/sequencify.ts
Normal file
40
packages/designer/src/plugin/sequencify.ts
Normal file
@ -0,0 +1,40 @@
|
||||
function sequence(tasks, names, results, missing, recursive, nest) {
|
||||
names.forEach((name) => {
|
||||
if (results.indexOf(name) !== -1) {
|
||||
return; // de-dup results
|
||||
}
|
||||
const node = tasks[name];
|
||||
if (!node) {
|
||||
missing.push(name);
|
||||
} else if (nest.indexOf(name) > -1) {
|
||||
nest.push(name);
|
||||
recursive.push(nest.slice(0));
|
||||
nest.pop(name);
|
||||
} else if (node.dep.length) {
|
||||
nest.push(name);
|
||||
sequence(tasks, node.dep, results, missing, recursive, nest); // recurse
|
||||
nest.pop(name);
|
||||
}
|
||||
results.push(name);
|
||||
});
|
||||
}
|
||||
|
||||
// tasks: object with keys as task names
|
||||
// names: array of task names
|
||||
export default function (tasks, names) {
|
||||
let results = []; // the final sequence
|
||||
const missing = []; // missing tasks
|
||||
const recursive = []; // recursive task dependencies
|
||||
|
||||
sequence(tasks, names, results, missing, recursive, []);
|
||||
|
||||
if (missing.length || recursive.length) {
|
||||
results = []; // results are incomplete at best, completely wrong at worst, remove them to avoid confusion
|
||||
}
|
||||
|
||||
return {
|
||||
sequence: results,
|
||||
missingTasks: missing,
|
||||
recursiveDependencies: recursive,
|
||||
};
|
||||
}
|
||||
4
packages/designer/src/utils/index.ts
Normal file
4
packages/designer/src/utils/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './invariant';
|
||||
export * from './slot';
|
||||
export * from './tree';
|
||||
export * from './logger';
|
||||
5
packages/designer/src/utils/invariant.ts
Normal file
5
packages/designer/src/utils/invariant.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export function invariant(check: any, message: string, thing?: any) {
|
||||
if (!check) {
|
||||
throw new Error('[designer] Invariant failed: ' + message + (thing ? ` in '${thing}'` : ''));
|
||||
}
|
||||
}
|
||||
7
packages/designer/src/utils/logger.ts
Normal file
7
packages/designer/src/utils/logger.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import Logger, { Level } from 'zen-logger';
|
||||
|
||||
export { Logger };
|
||||
|
||||
export function getLogger(config: { level: Level, bizName: string }): Logger {
|
||||
return new Logger(config);
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { isJSBlock, isJSExpression, isJSSlot } from '@ali/lowcode-types';
|
||||
import { isPlainObject, hasOwnProperty, cloneDeep, isI18NObject, isUseI18NSetter, convertToI18NObject, isString } from '@ali/lowcode-utils';
|
||||
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||
import { Designer, LiveEditing, TransformStage, Node, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||
import { Designer, LiveEditing, TransformStage, Node, getConvertedExtraKey, LowCodePluginManager } from '@ali/lowcode-designer';
|
||||
import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
||||
import bus from './bus';
|
||||
import { VE_EVENTS } from './base/const';
|
||||
@ -35,6 +35,9 @@ export const designer = new Designer({ editor });
|
||||
editor.set(Designer, designer);
|
||||
editor.set('designer', designer);
|
||||
|
||||
export const plugins = (new LowCodePluginManager(editor)).toProxy();
|
||||
editor.set('plugins', plugins);
|
||||
|
||||
designer.project.onCurrentDocumentChange((doc) => {
|
||||
bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY);
|
||||
editor.set('currentDocument', doc);
|
||||
|
||||
@ -9,12 +9,13 @@ import {
|
||||
registerMetadataTransducer,
|
||||
addBuiltinComponentAction,
|
||||
removeBuiltinComponentAction,
|
||||
modifyBuiltinComponentAction,
|
||||
ILowCodePluginContext,
|
||||
// modifyBuiltinComponentAction,
|
||||
} from '@ali/lowcode-designer';
|
||||
import { createElement } from 'react';
|
||||
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS, VERSION as Version } from './base/const';
|
||||
import Bus from './bus';
|
||||
import { skeleton, designer, editor } from './editor';
|
||||
import { skeleton, designer, editor, plugins } from './editor';
|
||||
import { Workbench } from '@ali/lowcode-editor-skeleton';
|
||||
import Panes from './panes';
|
||||
import Exchange from './exchange';
|
||||
@ -37,13 +38,14 @@ import '@ali/lowcode-editor-setters';
|
||||
|
||||
import './vision.less';
|
||||
|
||||
function init(container?: Element) {
|
||||
async function init(container?: Element) {
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
container.id = 'engine';
|
||||
|
||||
await plugins.init();
|
||||
render(
|
||||
createElement(Workbench, {
|
||||
skeleton,
|
||||
@ -125,6 +127,7 @@ const VisualEngine = {
|
||||
logger,
|
||||
Symbols,
|
||||
registerMetadataTransducer,
|
||||
plugins,
|
||||
// Flags,
|
||||
};
|
||||
|
||||
@ -177,12 +180,13 @@ export {
|
||||
logger,
|
||||
Symbols,
|
||||
registerMetadataTransducer,
|
||||
plugins,
|
||||
};
|
||||
|
||||
const version = '6.0.0 (LowcodeEngine 0.9.32)';
|
||||
const version = '1.0.28';
|
||||
|
||||
console.log(
|
||||
`%c VisionEngine %c v${version} `,
|
||||
`%c AliLowCodeEngine %c v${version} `,
|
||||
'padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060; font-weight: bold;',
|
||||
'padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e; font-weight: bold;',
|
||||
);
|
||||
|
||||
@ -69,7 +69,7 @@ function propTypeToSetter(propType: PropType): SetterType {
|
||||
const componentName = dataSource.length >= 4 ? 'SelectSetter' : 'RadioGroupSetter';
|
||||
return {
|
||||
componentName,
|
||||
props: { dataSource },
|
||||
props: { dataSource, options: dataSource },
|
||||
isRequired,
|
||||
initialValue: dataSource[0] ? dataSource[0].value : null,
|
||||
};
|
||||
@ -148,6 +148,8 @@ function propTypeToSetter(propType: PropType): SetterType {
|
||||
},
|
||||
isRequired,
|
||||
};
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
return {
|
||||
componentName: 'MixedSetter',
|
||||
|
||||
@ -17,3 +17,4 @@ export * from './node';
|
||||
export * from './transform-stage';
|
||||
export * from './code-intermediate';
|
||||
export * from './code-result';
|
||||
export * from './plugin';
|
||||
|
||||
54
packages/types/src/plugin.ts
Normal file
54
packages/types/src/plugin.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { CompositeObject } from '@ali/lowcode-types';
|
||||
import Logger from 'zen-logger';
|
||||
import { Skeleton } from '@ali/lowcode-editor-skeleton';
|
||||
import { Editor, Hotkey } from '@ali/lowcode-editor-core';
|
||||
|
||||
export interface ILowCodePluginConfig {
|
||||
manager: ILowCodePluginManager;
|
||||
name: string;
|
||||
dep: string[]; // 依赖插件名
|
||||
init(): void;
|
||||
destroy(): void;
|
||||
exports(): CompositeObject;
|
||||
}
|
||||
|
||||
export interface ILowCodePlugin {
|
||||
name: string;
|
||||
dep: string[];
|
||||
disabled: boolean;
|
||||
config: ILowCodePluginConfig;
|
||||
logger: Logger;
|
||||
emit(): void;
|
||||
on(): void;
|
||||
init(): void;
|
||||
destroy(): void;
|
||||
toProxy(): any;
|
||||
setDisabled(flag: boolean): void;
|
||||
}
|
||||
|
||||
export interface ILowCodePluginContext {
|
||||
skeleton: Skeleton;
|
||||
editor: Editor;
|
||||
plugins: ILowCodePluginManager;
|
||||
hotkey: Hotkey;
|
||||
logger: Logger;
|
||||
/**
|
||||
其他暂不增加,按需增加
|
||||
*/
|
||||
}
|
||||
|
||||
export interface ILowCodePluginManager {
|
||||
register(
|
||||
pluginConfig: (ctx: ILowCodePluginContext, options: CompositeObject) => ILowCodePluginConfig,
|
||||
options: CompositeObject,
|
||||
): void;
|
||||
get(pluginName: string): ILowCodePlugin;
|
||||
getAll(): ILowCodePlugin[];
|
||||
has(pluginName: string): boolean;
|
||||
delete(pluginName: string): boolean;
|
||||
setDisabled(pluginName: string, flag: boolean): void;
|
||||
dispose(): void;
|
||||
/**
|
||||
后续可以补充插件操作,比如 disable / enable 之类的
|
||||
*/
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user