Skip to content

Commit

Permalink
feat: add router
Browse files Browse the repository at this point in the history
  • Loading branch information
1ncounter committed Mar 21, 2024
1 parent fb5de64 commit 03f7c76
Show file tree
Hide file tree
Showing 51 changed files with 1,143 additions and 1,426 deletions.
1 change: 0 additions & 1 deletion docs/docs/specs/lowcode-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1513,7 +1513,6 @@ webpack.config.js # 项目工程配置,包含插件配置及自定义 webpack
| -------------- | ---------------------------------- | ------ | ------ | ------ | ---------------------------------------------- |
| path | 当前解析后的路径 | String | - | - | 必填 |
| hash | 当前路径的 hash 值,以 # 开头 | String | - | - | 必填 |
| href | 当前的全部路径 | String | - | - | 必填 |
| params | 匹配到的路径参数 | Object | - | - | 必填 |
| query | 当前的路径 query 对象 | Object | - | - | 必填,代表当前地址的 search 属性的对象 |
| name | 匹配到的路由记录名 | String | - | - | 选填 |
Expand Down
4 changes: 3 additions & 1 deletion runtime/renderer-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
"homepage": "https://github.com/alibaba/lowcode-engine/#readme",
"license": "MIT",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "",
"build": "tsc",
"test": "vitest --run",
"test:watch": "vitest"
},
Expand Down
10 changes: 5 additions & 5 deletions runtime/renderer-core/src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface AppBase {
*/
export interface AppContext {
schema: AppSchema;
config: Map<string, any>;
config: PlainObject;
appScope: CodeScope;
packageManager: PackageManager;
boosts: AppBoostsManager;
Expand All @@ -35,14 +35,14 @@ type AppCreator<O, T extends AppBase> = (

export type App<T extends AppBase = AppBase> = {
schema: Project;
config: Map<string, any>;
config: PlainObject;
readonly boosts: AppBoosts;

use(plugin: Plugin): Promise<void>;
} & T;

/**
* 创建应用
* 创建 createApp 的辅助函数
* @param schema
* @param options
* @returns
Expand All @@ -55,9 +55,9 @@ export function createAppFunction<O extends AppOptionsBase, T extends AppBase =
}

return async (options) => {
const { schema, appScopeValue = {} } = options;
const { schema, appScopeValue } = options;
const appSchema = createAppSchema(schema);
const appConfig = new Map<string, any>();
const appConfig = {};
const packageManager = createPackageManager();
const appScope = createScope({
...appScopeValue,
Expand Down
170 changes: 38 additions & 132 deletions runtime/renderer-core/src/api/component.ts
Original file line number Diff line number Diff line change
@@ -1,136 +1,42 @@
import { CreateContainerOptions, createContainer } from '../container';
import { createCodeRuntime, createScope } from '../code-runtime';
import { throwRuntimeError } from '../utils/error';
import { validateContainerSchema } from '../validator/schema';

export interface ComponentOptionsBase<C> {
componentsTree: RootSchema;
componentsRecord: Record<string, C | Package>;
dataSourceCreator: DataSourceCreator;
}

export function createComponentFunction<C, O extends ComponentOptionsBase<C>>(options: {
stateCreator: (initState: AnyObject) => StateContext;
componentCreator: (container: Container, componentOptions: O) => C;
defaultOptions?: Partial<O>;
}): (componentOptions: O) => C {
const { stateCreator, componentCreator, defaultOptions = {} } = options;

import { type CreateContainerOptions, createContainer, type Container } from '../container';
import type { PlainObject, InstanceStateApi } from '../types';

export type CreateComponentBaseOptions<T extends string> = Omit<
CreateContainerOptions<T>,
'stateCreator'
>;

/**
* 创建 createComponent 的辅助函数
* createComponent = createComponentFunction(() => component)
*/
export function createComponentFunction<
ComponentT,
InstanceT,
LifeCycleNameT extends string,
O extends CreateComponentBaseOptions<LifeCycleNameT>,
>(
stateCreator: (initState: PlainObject) => InstanceStateApi,
componentCreator: (
container: Container<InstanceT, LifeCycleNameT>,
componentOptions: O,
) => ComponentT,
): (componentOptions: O) => ComponentT {
return (componentOptions) => {
const finalOptions = Object.assign({}, defaultOptions, componentOptions);
const { supCodeScope, initScopeValue = {}, dataSourceCreator } = finalOptions;

const codeRuntimeScope =
supCodeScope?.createSubScope(initScopeValue) ?? createScope(initScopeValue);
const codeRuntime = createCodeRuntime(codeRuntimeScope);

const container: Container = {
get codeScope() {
return codeRuntimeScope;
},
get codeRuntime() {
return codeRuntime;
},

createInstance(componentsTree, extraProps = {}) {
if (!validateContainerSchema(componentsTree)) {
throwRuntimeError('createComponent', 'componentsTree is not valid!');
}

const mapRefToComponentInstance: Map<string, C> = new Map();

const initialState = codeRuntime.parseExprOrFn(componentsTree.state ?? {});
const stateContext = stateCreator(initialState);

codeRuntimeScope.setValue(
Object.assign(
{
props: codeRuntime.parseExprOrFn({
...componentsTree.defaultProps,
...componentsTree.props,
...extraProps,
}),
$(ref: string) {
return mapRefToComponentInstance.get(ref);
},
},
stateContext,
dataSourceCreator
? dataSourceCreator(componentsTree.dataSource ?? ({ list: [] } as any), stateContext)
: {},
) as ContainerInstanceScope<C>,
true,
);

if (componentsTree.methods) {
for (const [key, fn] of Object.entries(componentsTree.methods)) {
const customMethod = codeRuntime.createFnBoundScope(fn.value);
if (customMethod) {
codeRuntimeScope.inject(key, customMethod);
}
}
}

triggerLifeCycle('constructor');

function triggerLifeCycle(lifeCycleName: LifeCycleNameT, ...args: any[]) {
// keys 用来判断 lifeCycleName 存在于 schema 对象上,不获取原型链上的对象
if (
!componentsTree.lifeCycles ||
!Object.keys(componentsTree.lifeCycles).includes(lifeCycleName)
) {
return;
}

const lifeCycleSchema = componentsTree.lifeCycles[lifeCycleName];
if (isJsFunction(lifeCycleSchema)) {
const lifeCycleFn = codeRuntime.createFnBoundScope(lifeCycleSchema.value);
if (lifeCycleFn) {
lifeCycleFn.apply(codeRuntime.getScope().value, args);
}
}
}

const instance: ContainerInstance<C> = {
get id() {
return componentsTree.id;
},
get cssText() {
return componentsTree.css;
},
get codeScope() {
return codeRuntimeScope;
},

triggerLifeCycle,
setRefInstance(ref, instance) {
mapRefToComponentInstance.set(ref, instance);
},
removeRefInstance(ref) {
mapRefToComponentInstance.delete(ref);
},
getComponentTreeNodes() {
const childNodes = componentsTree.children
? Array.isArray(componentsTree.children)
? componentsTree.children
: [componentsTree.children]
: [];
const treeNodes = childNodes.map((item) => {
return createComponentTreeNode(item, undefined);
});

return treeNodes;
},

destory() {
mapRefToComponentInstance.clear();
codeRuntimeScope.setValue({});
},
};

return instance;
},
};
const {
supCodeScope,
initScopeValue = {},
dataSourceCreator,
componentsTree,
} = componentOptions;

const container = createContainer<InstanceT, LifeCycleNameT>({
supCodeScope,
initScopeValue,
stateCreator,
dataSourceCreator,
componentsTree,
});

return componentCreator(container, componentOptions);
};
Expand Down
51 changes: 24 additions & 27 deletions runtime/renderer-core/src/code-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,20 @@ export function createCodeRuntime(scopeOrValue: PlainObject = {}): CodeRuntime {
};
}

export interface CodeScope {
readonly value: PlainObject;
export interface CodeScope<T extends PlainObject = PlainObject, K extends keyof T = keyof T> {
readonly value: T;

inject(name: string, value: any, force?: boolean): void;
setValue(value: PlainObject, replace?: boolean): void;
createSubScope(initValue?: PlainObject): CodeScope;
inject(name: K, value: T[K], force?: boolean): void;
setValue(value: T, replace?: boolean): void;
createSubScope<O extends PlainObject = PlainObject>(initValue: O): CodeScope<T & O>;
}

export function createScope(initValue: PlainObject = {}): CodeScope {
export function createScope<T extends PlainObject = PlainObject, K extends keyof T = keyof T>(
initValue: T,
): CodeScope<T, K> {
const innerScope = { value: initValue };
const proxyValue = new Proxy(Object.create(null), {

const proxyValue: T = new Proxy(Object.create(null), {
set(target, p, newValue, receiver) {
return Reflect.set(target, p, newValue, receiver);
},
Expand All @@ -104,37 +107,31 @@ export function createScope(initValue: PlainObject = {}): CodeScope {
},
});

function inject(name: string, value: any, force = false): void {
if (innerScope.value[name] && !force) {
console.warn(`${name} 已存在值`);
return;
}

innerScope.value[name] = value;
}

function createSubScope(initValue: PlainObject = {}) {
const childScope = createScope(initValue);

(childScope as any).__raw.__parent = innerScope;

return childScope;
}

const scope: CodeScope = {
const scope: CodeScope<T, K> = {
get value() {
// dev return value
return proxyValue;
},
inject,
inject(name, value, force = false): void {
if (innerScope.value[name] && !force) {
return;
}
innerScope.value[name] = value;
},
setValue(value, replace = false) {
if (replace) {
innerScope.value = { ...value };
} else {
innerScope.value = Object.assign({}, innerScope.value, value);
}
},
createSubScope,
createSubScope<O extends PlainObject = PlainObject>(initValue: O) {
const childScope = createScope<O & T>(initValue);

(childScope as any).__raw.__parent = innerScope;

return childScope;
},
};

Object.defineProperty(scope, Symbol.for(SYMBOL_SIGN), { get: () => true });
Expand Down
10 changes: 8 additions & 2 deletions runtime/renderer-core/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
ComponentTree,
InstanceDataSourceApi,
InstanceStateApi,
NodeType,
} from './types';
import { type CodeScope, type CodeRuntime, createCodeRuntime, createScope } from './code-runtime';
import { isJSFunction } from './utils/type-guard';
Expand Down Expand Up @@ -48,7 +49,13 @@ export interface CreateContainerOptions<LifeCycleNameT extends string> {
export function createContainer<InstanceT, LifeCycleNameT extends string>(
options: CreateContainerOptions<LifeCycleNameT>,
): Container<InstanceT, LifeCycleNameT> {
const { componentsTree, supCodeScope, initScopeValue, stateCreator, dataSourceCreator } = options;
const {
componentsTree,
supCodeScope,
initScopeValue = {},
stateCreator,
dataSourceCreator,
} = options;

validContainerSchema(componentsTree);

Expand Down Expand Up @@ -151,7 +158,6 @@ export function createContainer<InstanceT, LifeCycleNameT extends string>(

createWidgets<Element>() {
if (!componentsTree.children) return [];

return componentsTree.children.map((item) => createWidget<Element>(item));
},
};
Expand Down
7 changes: 5 additions & 2 deletions runtime/renderer-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ export { createCodeRuntime, createScope } from './code-runtime';
export { definePlugin } from './plugin';
export { createWidget } from './widget';
export { createContainer } from './container';
export { createHookStore, useEvent } from './utils/hook';
export * from './utils/type-guard';
export * from './utils/value';
export * from './widget';

/* --------------- types ---------------- */
export * from './types';
export type { CodeRuntime, CodeScope } from './code-runtime';
export type { Plugin, PluginSetupContext } from './plugin';
export type { PackageManager, PackageLoader } from './package';
export type { Container } from './container';
export type { Widget, TextWidget, ComponentWidget } from './widget';
export type { Container, CreateContainerOptions } from './container';
6 changes: 4 additions & 2 deletions runtime/renderer-core/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ export interface PackageManager {
/** 解析组件映射 */
resolveComponentMaps(componentMaps: ComponentMap[]): void;
/** 获取组件映射对象,key = componentName value = component */
getComponentsNameRecord<C = unknown>(componentMaps?: ComponentMap[]): Record<string, C>;
getComponentsNameRecord<C = unknown>(
componentMaps?: ComponentMap[],
): Record<string, C | LowCodeComponent>;
/** 通过组件名获取对应的组件 */
getComponent<C = unknown>(componentName: string): C | undefined;
getComponent<C = unknown>(componentName: string): C | LowCodeComponent | undefined;
/** 注册组件 */
registerComponentByName(componentName: string, Component: unknown): void;
}
Expand Down
Loading

0 comments on commit 03f7c76

Please sign in to comment.