fix: fix some bugs

This commit is contained in:
1ncounter 2024-06-01 09:32:18 +08:00
parent f1711e0fc9
commit 0157bbd68f
28 changed files with 272 additions and 292 deletions

View File

@ -3,7 +3,6 @@
"private": true,
"type": "module",
"scripts": {
"playground": "pnpm --filter playground dev",
"build": "node ./scripts/build.js",
"test": "vitest",
"clean": "rimraf ./packages/*/dist",
@ -35,6 +34,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"globals": "^15.0.0",
"husky": "^9.0.11",
"jsdom": "^24.1.0",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5",
"rimraf": "^5.0.2",

View File

@ -26,6 +26,11 @@ function RouteOutlet({ pageConfig }: OutletProps) {
const context = useRenderContext();
const { schema, packageManager } = context;
const { type = 'lowCode', mappingId } = pageConfig;
console.log(
'%c [ pageConfig ]-29',
'font-size:13px; background:pink; color:#bf2c9f;',
pageConfig,
);
if (type === 'lowCode') {
// 在页面渲染时重新获取 componentsMap

View File

@ -13,12 +13,12 @@ export const createRouterProvider = (router: Router) => {
return () => remove();
}, []);
const pageSchema = useMemo(() => {
const pageConfig = useMemo(() => {
const pages = schema.get('pages') ?? [];
const matched = location.matched[location.matched.length - 1];
if (matched) {
const page = pages.find((item) => matched.page === item.id);
const page = pages.find((item) => matched.page === item.mappingId);
return page;
}
@ -28,7 +28,7 @@ export const createRouterProvider = (router: Router) => {
return (
<RouterContext.Provider value={router}>
<RouteLocationContext.Provider value={location}>
<PageConfigContext.Provider value={pageSchema}>{children}</PageConfigContext.Provider>
<PageConfigContext.Provider value={pageConfig}>{children}</PageConfigContext.Provider>
</RouteLocationContext.Provider>
</RouterContext.Provider>
);

View File

@ -1 +1,5 @@
export const dataSourceCreator = () => ({}) as any;
export const dataSourceCreator = () =>
({
dataSourceMap: {},
reloadDataSource: () => {},
}) as any;

View File

@ -1,15 +1,14 @@
import { processValue, someValue } from '@alilc/lowcode-renderer-core';
import {
watch,
isJSExpression,
isJSFunction,
isJSSlot,
invariant,
isLowCodeComponentSchema,
isLowCodeComponentPackage,
isJSI18nNode,
} from '@alilc/lowcode-shared';
import { forwardRef, useRef, useEffect, createElement, memo } from 'react';
import { appendExternalStyle } from '../utils/element';
import { appendExternalStyle } from '../../../../playground/renderer/src/plugin/remote/element';
import { reactive } from '../utils/reactive';
import { useRenderContext } from '../context/render';
import { reactiveStateCreator } from './reactiveState';
@ -63,7 +62,7 @@ export function getComponentByName(
invariant(result, `${name} component not found in componentsRecord`);
if (isLowCodeComponentSchema(result)) {
if (isLowCodeComponentPackage(result)) {
const { componentsMap, componentsTree, utils, i18n } = result.schema;
if (componentsMap.length > 0) {
@ -120,6 +119,7 @@ export function createComponentBySchema(
}
const model = modelRef.current;
console.log('%c [ model ]-123', 'font-size:13px; background:pink; color:#bf2c9f;', model);
const isConstructed = useRef(false);
const isMounted = useRef(false);
@ -133,7 +133,7 @@ export function createComponentBySchema(
const scopeValue = model.codeScope.value;
// init dataSource
scopeValue.reloadDataSource();
scopeValue.reloadDataSource?.();
let styleEl: HTMLElement | undefined;
const cssText = model.getCssText();
@ -148,11 +148,11 @@ export function createComponentBySchema(
model.triggerLifeCycle('componentDidMount');
// 当 state 改变之后调用
const unwatch = watch(scopeValue.state, (_, oldVal) => {
if (isMounted.current) {
model.triggerLifeCycle('componentDidUpdate', props, oldVal);
}
});
// const unwatch = watch(scopeValue.state, (_, oldVal) => {
// if (isMounted.current) {
// model.triggerLifeCycle('componentDidUpdate', props, oldVal);
// }
// });
isMounted.current = true;
@ -160,7 +160,7 @@ export function createComponentBySchema(
// componentWillUnmount?.();
model.triggerLifeCycle('componentWillUnmount');
styleEl?.parentNode?.removeChild(styleEl);
unwatch();
// unwatch();
isMounted.current = false;
};
}, []);
@ -247,7 +247,7 @@ function createElementByWidget(
};
// 先将 jsslot, jsFunction 对象转换
const finalProps = processValue(
let finalProps = processValue(
componentProps,
(node) => isJSFunction(node) || isJSSlot(node),
(node: Spec.JSSlot | Spec.JSFunction) => {
@ -288,6 +288,17 @@ function createElementByWidget(
},
);
finalProps = processValue(
finalProps,
(value) => {
return value.type === 'JSSlot' && !value.value;
},
(node) => {
console.log('%c [ node ]-303', 'font-size:13px; background:pink; color:#bf2c9f;', node);
return null;
},
);
const childElements = children?.map((child) =>
createElementByWidget(child, codeScope, renderContext, componentRefAttached),
);

View File

@ -1,158 +0,0 @@
const addLeadingSlash = (path: string): string => {
return path.charAt(0) === '/' ? path : `/${path}`;
};
export function getElementById(id: string, tag: string = 'div') {
let el = document.getElementById(id);
if (!el) {
el = document.createElement(tag);
el.id = id;
}
return el;
}
const enum AssetType {
STYLE = 'style',
SCRIPT = 'script',
UNKONWN = 'unkonwn',
}
function getAssetTypeByUrl(url: string): AssetType {
const IS_CSS_REGEX = /\.css(\?((?!\.js$).)+)?$/;
const IS_JS_REGEX = /\.[t|j]sx?(\?((?!\.css$).)+)?$/;
const IS_JSON_REGEX = /\.json$/;
if (IS_CSS_REGEX.test(url)) {
return AssetType.STYLE;
} else if (IS_JS_REGEX.test(url) || IS_JSON_REGEX.test(url)) {
return AssetType.SCRIPT;
}
return AssetType.UNKONWN;
}
export async function loadPackageUrls(urls: string[]) {
const styles: string[] = [];
const scripts: string[] = [];
for (const url of urls) {
const type = getAssetTypeByUrl(url);
if (type === AssetType.SCRIPT) {
scripts.push(url);
} else if (type === AssetType.STYLE) {
styles.push(url);
}
}
await Promise.all(styles.map((item) => appendExternalCss(item)));
await Promise.all(scripts.map((item) => appendExternalScript(item)));
}
async function appendExternalScript(
url: string,
root: HTMLElement = document.body,
): Promise<HTMLElement> {
if (url) {
const el = getIfExistAssetByUrl(url, 'script');
if (el) return el;
}
return new Promise((resolve, reject) => {
const scriptElement = document.createElement('script');
// scriptElement.type = moduleType === 'module' ? 'module' : 'text/javascript';
/**
* `async=false` is required to make sure all js resources execute sequentially.
*/
scriptElement.async = false;
scriptElement.crossOrigin = 'anonymous';
scriptElement.src = url;
scriptElement.addEventListener(
'load',
() => {
resolve(scriptElement);
},
false,
);
scriptElement.addEventListener('error', (error) => {
if (root.contains(scriptElement)) {
root.removeChild(scriptElement);
}
reject(error);
});
root.appendChild(scriptElement);
});
}
async function appendExternalCss(
url: string,
root: HTMLElement = document.head,
): Promise<HTMLElement> {
if (url) {
const el = getIfExistAssetByUrl(url, 'link');
if (el) return el;
}
return new Promise((resolve, reject) => {
const el: HTMLLinkElement = document.createElement('link');
el.rel = 'stylesheet';
el.href = url;
el.addEventListener(
'load',
() => {
resolve(el);
},
false,
);
el.addEventListener('error', (error) => {
reject(error);
});
root.appendChild(el);
});
}
export async function appendExternalStyle(
cssText: string,
root: HTMLElement = document.head,
): Promise<HTMLElement> {
return new Promise((resolve, reject) => {
const el: HTMLStyleElement = document.createElement('style');
el.innerText = cssText;
el.addEventListener(
'load',
() => {
resolve(el);
},
false,
);
el.addEventListener('error', (error) => {
reject(error);
});
root.appendChild(el);
});
}
function getIfExistAssetByUrl(
url: string,
tag: 'link' | 'script',
): HTMLLinkElement | HTMLScriptElement | undefined {
return Array.from(document.getElementsByTagName(tag)).find((item) => {
const elUrl = (item as HTMLLinkElement).href || (item as HTMLScriptElement).src;
if (/^(https?:)?\/\/([\w.]+\/?)\S*/gi.test(url)) {
// if url === http://xxx.xxx
return url === elUrl;
} else {
// if url === /xx/xx/xx.xx
return `${location.origin}${addLeadingSlash(url)}` === elUrl;
}
});
}

View File

@ -10,5 +10,6 @@ export function normalizeComponentNode(node: Spec.ComponentNode): NormalizedComp
...node,
loopArgs: node.loopArgs ?? ['item', 'index'],
props: node.props ?? {},
condition: node.condition || node.condition === false ? node.condition : true,
};
}

View File

@ -3,5 +3,5 @@
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"]
"include": ["src", "../../playground/renderer/src/plugin/remote/element.ts"]
}

View File

@ -1,17 +0,0 @@
import { describe, expect, it } from 'vitest';
import { appBoosts } from '../src/boosts';
describe('appBoosts', () => {
it('should add value successfully', () => {
appBoosts.add('test', 1);
expect(appBoosts.value.test).toBe(1);
});
it('should clear removed value', () => {
appBoosts.add('test', 1);
expect(appBoosts.value.test).toBe(1);
appBoosts.remove('test');
expect(appBoosts.value.test).toBeUndefined();
});
});

View File

@ -1 +0,0 @@
import {} from 'vitest';

View File

@ -1,2 +0,0 @@
import { expect } from 'vitest';
import { createPackageManager } from '../src/package';

View File

@ -0,0 +1,46 @@
import { describe, it, expect, beforeAll } from 'vitest';
import { ICodeScope, CodeScope } from '../../../src/parts/code-runtime';
describe('codeScope', () => {
let scope: ICodeScope;
beforeAll(() => {
scope = new CodeScope({});
});
it('should inject a new value', () => {
scope.inject('username', 'Alice');
expect(scope.value).toEqual({ username: 'Alice' });
});
it('should not overwrite an existing value without force', () => {
scope.inject('username', 'Bob');
expect(scope.value).toEqual({ username: 'Alice' });
});
it('should overwrite an existing value with force', () => {
scope.inject('username', 'Bob', true);
expect(scope.value).toEqual({ username: 'Bob' });
});
it('should set new value without replacing existing values', () => {
scope.setValue({ age: 25 });
expect(scope.value).toEqual({ username: 'Bob', age: 25 });
});
it('should set new value and replace all existing values', () => {
scope.setValue({ loggedIn: true }, true);
expect(scope.value).toEqual({ loggedIn: true });
});
it('should create a child scope with initial values', () => {
const childScope = scope.createChild({ sessionId: 'abc123' });
expect(childScope.value).toEqual({ loggedIn: true, sessionId: 'abc123' });
});
it('should set new values in the child scope without affecting the parent scope', () => {
const childScope = scope.createChild({ theme: 'dark' });
expect(childScope.value).toEqual({ loggedIn: true, sessionId: 'abc123', theme: 'dark' });
expect(scope.value).toEqual({ loggedIn: true });
});
});

View File

@ -1,12 +0,0 @@
import { describe, it, expect, expectTypeOf } from 'vitest';
import { definePlugin, type Plugin, createPluginManager } from '../src/plugin';
describe('createPluginManager', () => {
it('should install plugin successfully', () => {});
it('should install plugins when deps installed', () => {});
});
describe('definePlugin', () => {
it('should return a plugin', () => {});
});

View File

@ -1,19 +0,0 @@
import { describe, it, expect } from 'vitest';
import { nonSetterProxy } from '../../src/utils/non-setter-proxy';
describe('nonSetterProxy', () => {
it('should non setter on proxy', () => {
const target = { a: 1 };
const proxy = nonSetterProxy(target);
expect(() => ((proxy as any).b = 1)).toThrowError(/trap returned falsish for property 'b'/);
});
it('should correct value when getter', () => {
const target = { a: 1 };
const proxy = nonSetterProxy(target);
expect(proxy.a).toBe(1);
expect('a' in proxy).toBeTruthy();
});
});

View File

@ -32,6 +32,8 @@ export class RendererMain {
// valid schema
this.schemaService.initialize(schema);
this.codeRuntimeService.initialize(options);
// init intl
const finalLocale = options.locale ?? navigator.language;
const i18nTranslations = this.schemaService.get('i18n') ?? {};

View File

@ -11,25 +11,43 @@ import {
import { type ICodeScope, CodeScope } from './codeScope';
import { processValue } from '../../utils/value';
type BeforeResolveCb = (
code: Spec.JSExpression | Spec.JSFunction,
) => Spec.JSExpression | Spec.JSFunction;
export interface ICodeRuntimeService {
initialize({ evalCodeFunction }: CodeRuntimeInitializeOptions): void;
getScope(): ICodeScope;
run<R = unknown>(code: string, scope?: ICodeScope): R | undefined;
resolve(value: PlainObject, scope?: ICodeScope): any;
beforeRun(fn: (code: string) => string): EventDisposable;
beforeResolve(fn: BeforeResolveCb): EventDisposable;
createChildScope(value: PlainObject): ICodeScope;
}
export const ICodeRuntimeService = createDecorator<ICodeRuntimeService>('codeRuntimeService');
export type EvalCodeFunction = (code: string, scope: any) => any;
export interface CodeRuntimeInitializeOptions {
evalCodeFunction?: EvalCodeFunction;
}
@Provide(ICodeRuntimeService)
export class CodeRuntimeService implements ICodeRuntimeService {
private codeScope: ICodeScope = new CodeScope({});
private callbacks = createCallback<(code: string) => string>();
private callbacks = createCallback<BeforeResolveCb>();
private evalCodeFunction: EvalCodeFunction = evaluate;
initialize({ evalCodeFunction }: CodeRuntimeInitializeOptions) {
if (evalCodeFunction) this.evalCodeFunction = evalCodeFunction;
}
getScope() {
return this.codeScope;
@ -39,13 +57,7 @@ export class CodeRuntimeService implements ICodeRuntimeService {
if (!code) return undefined;
try {
const cbs = this.callbacks.list();
const finalCode = cbs.reduce((code, cb) => cb(code), code);
let result = new Function(
'scope',
`"use strict";return (function(){return (${finalCode})}).bind(scope)();`,
)(scope.value);
let result = this.evalCodeFunction(code, scope.value);
if (typeof result === 'function') {
result = result.bind(scope.value);
@ -66,17 +78,20 @@ export class CodeRuntimeService implements ICodeRuntimeService {
return isJSExpression(data) || isJSFunction(data);
},
(node: Spec.JSExpression | Spec.JSFunction) => {
const v = this.run(node.value, scope);
const cbs = this.callbacks.list();
const finalNode = cbs.reduce((node, cb) => cb(node), node);
if (typeof v === 'undefined' && (node as any).mock) {
return (node as any).mock;
const v = this.run(finalNode.value, scope);
if (typeof v === 'undefined' && finalNode.mock) {
return this.resolve(finalNode.mock, scope);
}
return v;
},
);
}
beforeRun(fn: (code: string) => string): EventDisposable {
beforeResolve(fn: BeforeResolveCb): EventDisposable {
return this.callbacks.add(fn);
}
@ -84,3 +99,9 @@ export class CodeRuntimeService implements ICodeRuntimeService {
return this.codeScope.createChild(value);
}
}
function evaluate(code: string, scope: any) {
return new Function('scope', `"use strict";return (function(){return (${code})}).bind(scope)();`)(
scope,
);
}

View File

@ -38,11 +38,23 @@ export class CodeScope implements ICodeScope {
if (Reflect.has(valueTarget.current, p)) {
return Reflect.get(valueTarget.current, p, receiver);
}
valueTarget = this.__node.prev;
valueTarget = valueTarget.prev;
}
return Reflect.get(target, p, receiver);
},
has: (target, p) => {
let valueTarget: IScopeNode | undefined = this.__node;
while (valueTarget) {
if (Reflect.has(valueTarget.current, p)) {
return true;
}
valueTarget = valueTarget.prev;
}
return Reflect.has(target, p);
},
});
}

View File

@ -1,11 +1,4 @@
import {
type Spec,
type PlainObject,
isJSFunction,
isComponentNode,
invariant,
type AnyFunction,
} from '@alilc/lowcode-shared';
import { type Spec, type PlainObject, isComponentNode, invariant } from '@alilc/lowcode-shared';
import { type ICodeScope, type ICodeRuntimeService } from '../code-runtime';
import { IWidget, Widget } from '../widget';
@ -118,8 +111,8 @@ export class ComponentTreeModel<Component, ComponentInstance = unknown>
);
for (const [key, fn] of Object.entries(methods)) {
const customMethod = this.codeRuntime.run(fn.value, this.codeScope);
if (customMethod) {
const customMethod = this.codeRuntime.resolve(fn, this.codeScope);
if (typeof customMethod === 'function') {
this.codeScope.inject(key, customMethod);
}
}
@ -140,11 +133,9 @@ export class ComponentTreeModel<Component, ComponentInstance = unknown>
const lifeCycleSchema = this.componentsTree.lifeCycles[lifeCycleName];
if (isJSFunction(lifeCycleSchema)) {
const lifeCycleFn = this.codeRuntime.run<AnyFunction>(lifeCycleSchema.value, this.codeScope);
if (lifeCycleFn) {
lifeCycleFn.apply(this.codeScope.value, args);
}
const lifeCycleFn = this.codeRuntime.resolve(lifeCycleSchema, this.codeScope);
if (typeof lifeCycleFn === 'function') {
lifeCycleFn.apply(this.codeScope.value, args);
}
}

View File

@ -1,6 +1,23 @@
import { type Spec, type LowCodeComponent, createDecorator, Provide } from '@alilc/lowcode-shared';
import {
type Spec,
type LowCodeComponent,
type ProCodeComponent,
createDecorator,
Provide,
isLowCodeComponentPackage,
isProCodeComponentPackage,
} from '@alilc/lowcode-shared';
import { get as lodashGet } from 'lodash-es';
import { PackageLoader } from './loader';
export interface NormalizedPackage {
id: string;
package: string;
library: string;
exportSource?: NormalizedPackage | undefined;
raw: ProCodeComponent;
}
export interface IPackageManagementService {
/**
*
@ -39,36 +56,30 @@ export class PackageManagementService implements IPackageManagementService {
private packageStore: Map<string, any> = ((window as any).__PACKAGE_STORE__ ??= new Map());
private packagesRef: Spec.Package[] = [];
private packagesMap: Map<string, NormalizedPackage> = new Map();
private lowCodeComponentPackages: Map<string, LowCodeComponent> = new Map();
private packageLoaders: PackageLoader[] = [];
async loadPackages(packages: Spec.Package[]) {
for (const item of packages) {
if (!item.package && !item.id) continue;
const newId = item.package ?? item.id!;
const isExist = this.packagesRef.some((_) => {
const itemId = _.package ?? _.id;
return itemId === newId;
});
if (!isExist) {
this.packagesRef.push(item);
if (!this.packageStore.has(newId)) {
const loader = this.packageLoaders.find((loader) => loader.active(item));
if (!loader) continue;
const result = await loader.load.call(this, item);
if (result) this.packageStore.set(newId, result);
}
// low code component not need load
if (isLowCodeComponentPackage(item)) {
this.lowCodeComponentPackages.set(item.id, item);
continue;
}
if (!isProCodeComponentPackage(item)) continue;
const normalized = this.normalizePackage(item);
await this.loadPackageByNormalized(normalized);
}
}
getPackageInfo(packageName: string) {
return this.packagesRef.find((p) => p.package === packageName);
return this.packagesMap.get(packageName)?.raw;
}
getLibraryByPackageName(packageName: string) {
@ -86,9 +97,7 @@ export class PackageManagementService implements IPackageManagementService {
resolveComponentMaps(componentMaps: Spec.ComponentMap[]) {
for (const map of componentMaps) {
if (map.devMode === 'lowCode') {
const packageInfo = this.packagesRef.find((_) => {
return _.id === (map as LowCodeComponent).id;
});
const packageInfo = this.lowCodeComponentPackages.get((map as LowCodeComponent).id);
if (packageInfo) {
this.componentsRecord[map.componentName] = packageInfo;
@ -123,7 +132,6 @@ export class PackageManagementService implements IPackageManagementService {
getComponentsNameRecord(componentMaps?: Spec.ComponentMap[]) {
if (componentMaps) {
const newMaps = componentMaps.filter((item) => !this.componentsRecord[item.componentName]);
this.resolveComponentMaps(newMaps);
}
@ -143,4 +151,72 @@ export class PackageManagementService implements IPackageManagementService {
this.packageLoaders.push(loader);
}
}
private normalizePackage(packageInfo: ProCodeComponent): NormalizedPackage {
if (this.packagesMap.has(packageInfo.package)) {
return this.packagesMap.get(packageInfo.package)!;
}
const normalized: NormalizedPackage = {
package: packageInfo.package,
id: packageInfo.id ?? packageInfo.package,
library: packageInfo.library,
raw: packageInfo,
};
this.packagesMap.set(normalized.package, normalized);
// add normalized to package exports dependency graph
const packagesRef = [...this.packagesMap.values()];
// set export source
if (packageInfo.exportSourceId || packageInfo.exportSourceLibrary) {
const found = packagesRef.find((item) => {
if (!packageInfo.exportSourceId) {
return item.library === packageInfo.exportSourceLibrary;
} else {
return item.package === packageInfo.exportSourceId;
}
});
if (found) {
normalized.exportSource = found;
}
}
return normalized;
}
private async loadPackageByNormalized(normalized: NormalizedPackage) {
if (this.packageStore.has(normalized.package)) return;
// if it has export source package, wait export source package loaded
if (normalized.exportSource) {
if (this.packageStore.has(normalized.package)) {
const library = lodashGet(window, normalized.library);
if (library) {
this.packageStore.set(normalized.package, library);
}
}
} else {
const loader = this.packageLoaders.find((loader) => loader.active(normalized.raw));
if (!loader) return;
const result = await loader.load.call(this, normalized.raw);
if (result) {
this.packageStore.set(normalized.package, result);
}
}
// if current package loaded, set the value of the dependency on this package
if (this.packageStore.has(normalized.package)) {
const chilren = [...this.packagesMap.values()].filter((item) => {
return item.exportSource?.package === normalized.package;
});
for (const child of chilren) {
await this.loadPackageByNormalized(child);
}
}
}
}

View File

@ -33,7 +33,7 @@ export class RuntimeUtilService implements IRuntimeUtilService {
}
} else {
const fn = this.parseUtil(name);
this.utilsMap.set(name.name, fn);
if (fn) this.utilsMap.set(name.name, fn);
}
}

View File

@ -3,6 +3,7 @@ import { type Plugin } from './parts/extension';
import { type ISchemaService } from './parts/schema';
import { type IPackageManagementService } from './parts/package';
import { type IExtensionHostService } from './parts/extension';
import { type EvalCodeFunction } from './parts/code-runtime';
export interface AppOptions {
schema: Spec.Project;
@ -18,6 +19,8 @@ export interface AppOptions {
*
*/
mode?: 'development' | 'production';
evalCodeFunction?: EvalCodeFunction;
}
export type RendererApplication<Render = unknown> = {

View File

@ -100,9 +100,10 @@ function nomarlizeLocale(target: Locale) {
return navigatorLanguageMapping[target];
}
const replaced = target.replace('_', '-');
const splited = replaced.split('-').slice(0, 2);
splited[1] = splited[1].toUpperCase();
// const replaced = target.replace('_', '-');
// const splited = replaced.split('-').slice(0, 2);
// splited[1] = splited[1].toUpperCase();
return splited.join('-');
// return splited.join('-');
return target;
}

View File

@ -4,6 +4,7 @@ import { Project } from './specs/lowcode-spec';
export interface ProCodeComponent extends Package {
package: string;
type: 'proCode';
library: string;
}
export interface LowCodeComponent extends Package {

View File

@ -56,7 +56,7 @@ export interface Package {
/**
* webpack output.library
*/
library: string;
library?: string | undefined;
/**
* window[exportName] Object
*/

View File

@ -423,6 +423,8 @@ export interface JSSlot {
type: 'JSSlot';
value: ComponentNode | ComponentNode[];
params?: string[];
[key: string]: any;
}
/**
@ -431,6 +433,8 @@ export interface JSSlot {
export interface JSFunction {
type: 'JSFunction';
value: string;
[key: string]: any;
}
/**
@ -439,6 +443,8 @@ export interface JSFunction {
export interface JSExpression {
type: 'JSExpression';
value: string;
[key: string]: any;
}
/**
@ -456,4 +462,6 @@ export interface JSI18n {
params?: Record<string, string | number | JSExpression>;
}
export type JSNode = JSSlot | JSExpression | JSExpression | JSI18n;
export type NodeType = string | JSExpression | JSI18n | ComponentNode;

View File

@ -1 +1,2 @@
export * from './spec';
export * from './material';

View File

@ -0,0 +1,10 @@
import { LowCodeComponent, ProCodeComponent } from '../../types';
import { isPlainObject } from 'lodash-es';
export function isLowCodeComponentPackage(v: unknown): v is LowCodeComponent {
return isPlainObject(v) && (v as any).type === 'lowCode' && (v as any).schema;
}
export function isProCodeComponentPackage(v: unknown): v is ProCodeComponent {
return isPlainObject(v) && (v as any).package && (v as any).library;
}

View File

@ -1,4 +1,4 @@
import type { Spec, LowCodeComponent } from '../../types';
import type { Spec } from '../../types';
import { isPlainObject } from 'lodash-es';
export function isJSExpression(v: unknown): v is Spec.JSExpression {
@ -24,7 +24,3 @@ export function isJSI18nNode(v: unknown): v is Spec.JSI18n {
export function isComponentNode(v: unknown): v is Spec.ComponentNode {
return isPlainObject(v) && (v as any).componentName;
}
export function isLowCodeComponentSchema(v: unknown): v is LowCodeComponent {
return isPlainObject(v) && (v as any).type === 'lowCode' && (v as any).schema;
}