From a855c05d67ec14ac68d3b5f030e90775931af43c Mon Sep 17 00:00:00 2001 From: 1ncounter <1ncounter.100@gmail.com> Date: Fri, 28 Jun 2024 18:04:01 +0800 Subject: [PATCH] fix: fix renderer some bugs --- packages/core/package.json | 10 +- packages/core/src/index.ts | 2 - packages/core/src/intl.ts | 154 ------------------ packages/react-renderer/src/api/app.tsx | 19 +-- packages/react-renderer/src/api/component.tsx | 5 +- packages/react-renderer/src/api/context.ts | 14 ++ packages/react-renderer/src/app/context.ts | 8 - packages/react-renderer/src/app/index.ts | 1 - packages/react-renderer/src/app/view.tsx | 8 +- packages/react-renderer/src/index.ts | 4 +- packages/react-renderer/src/router/index.ts | 2 +- packages/react-renderer/src/router/plugin.ts | 14 +- packages/react-renderer/src/router/route.tsx | 4 +- .../react-renderer/src/runtime/components.tsx | 15 +- .../react-renderer/src/runtime/dataSource.ts | 5 - .../react-renderer/src/runtime/schema.tsx | 18 +- packages/react-renderer/src/utils/element.ts | 4 - .../__tests__/services/lifeCycle.spec.ts | 6 +- packages/renderer-core/src/main.ts | 39 ++--- .../code-runtime/codeRuntimeService.ts | 9 +- .../src/services/code-runtime/codeScope.ts | 2 +- .../extension/extensionHostService.ts | 6 +- .../src/services/extension/plugin.ts | 6 +- .../src/services/lifeCycleService.ts | 47 +++--- .../src/services/model/componentTreeModel.ts | 26 ++- .../src/services/package/managementService.ts | 51 +++--- .../src/services/runtimeIntlService.ts | 10 +- .../src/services/runtimeUtilService.ts | 79 ++++++--- .../src/services/schema/schemaService.ts | 34 +++- packages/renderer-core/src/types.ts | 9 +- packages/renderer-router/src/router.ts | 11 +- packages/renderer-router/src/utils/url.ts | 11 ++ .../shared/src/types/specs/lowcode-spec.ts | 10 +- 33 files changed, 255 insertions(+), 388 deletions(-) delete mode 100644 packages/core/src/intl.ts create mode 100644 packages/react-renderer/src/api/context.ts delete mode 100644 packages/react-renderer/src/app/context.ts delete mode 100644 packages/react-renderer/src/runtime/dataSource.ts diff --git a/packages/core/package.json b/packages/core/package.json index cef3fa6e3..254d8b7cb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -34,13 +34,7 @@ }, "dependencies": { "@alilc/lowcode-shared": "workspace:*", - "@alilc/lowcode-types": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", - "@formatjs/intl": "^2.10.1", - "lodash-es": "^4.17.21", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "events": "^3.3.0" + "lodash-es": "^4.17.21" }, "devDependencies": { "@types/lodash-es": "^4.17.12", @@ -49,8 +43,6 @@ }, "peerDependencies": { "@alilc/lowcode-shared": "workspace:*", - "@alilc/lowcode-types": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9e4bb675a..7cd2bfba6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,2 @@ export * from './preference'; export * from './hotkey'; -export * from './intl'; -export * from './instantiation'; diff --git a/packages/core/src/intl.ts b/packages/core/src/intl.ts deleted file mode 100644 index 952930060..000000000 --- a/packages/core/src/intl.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { - signal, - computed, - effect, - createLogger, - type Spec, - type Signal, - type ComputedSignal, - type PlainObject, -} from '@alilc/lowcode-shared'; -import { createIntl, createIntlCache, type IntlShape as IntlFormatter } from '@formatjs/intl'; -import { mapKeys } from 'lodash-es'; - -export { IntlFormatter }; - -const logger = createLogger({ level: 'warn', bizName: 'globalLocale' }); - -/** - * todo: key 需要被统一 - */ -const STORED_LOCALE_KEY = 'ali-lowcode-config'; - -export type Locale = string; -export type IntlMessage = Spec.I18nMap[Locale]; -export type IntlMessageRecord = Spec.I18nMap; - -export class Intl { - #locale: Signal; - #messageStore: Signal; - #currentMessage: ComputedSignal; - #intlShape: IntlFormatter; - - constructor(defaultLocale?: string, messages: IntlMessageRecord = {}) { - if (defaultLocale) { - defaultLocale = nomarlizeLocale(defaultLocale); - } else { - defaultLocale = 'zh-CN'; - } - - const messageStore = mapKeys(messages, (_, key) => { - return nomarlizeLocale(key); - }); - - this.#locale = signal(defaultLocale); - this.#messageStore = signal(messageStore); - this.#currentMessage = computed(() => { - return this.#messageStore.value[this.#locale.value] ?? {}; - }); - - effect(() => { - const cache = createIntlCache(); - this.#intlShape = createIntl( - { - locale: this.#locale.value, - messages: this.#currentMessage.value, - }, - cache, - ); - }); - } - - getLocale() { - return this.#locale.value; - } - - setLocale(locale: Locale) { - const nomarlizedLocale = nomarlizeLocale(locale); - this.#locale.value = nomarlizedLocale; - } - - addMessages(locale: Locale, messages: IntlMessage) { - locale = nomarlizeLocale(locale); - const original = this.#messageStore.value[locale]; - - this.#messageStore.value[locale] = Object.assign(original, messages); - } - - getFormatter(): IntlFormatter { - return this.#intlShape; - } -} - -function initializeLocale() { - let result: Locale | undefined; - - let config: PlainObject = {}; - try { - // store 1: config from storage - config = JSON.parse(localStorage.getItem(STORED_LOCALE_KEY) || ''); - } catch { - // ignore; - } - if (config?.locale) { - result = (config.locale || '').replace('_', '-'); - logger.debug(`getting locale from localStorage: ${result}`); - } - - if (!result && navigator.language) { - // store 2: config from system - result = nomarlizeLocale(navigator.language); - } - - if (!result) { - logger.warn( - 'something when wrong when trying to get locale, use zh-CN as default, please check it out!', - ); - result = 'zh-CN'; - } - - return result; -} - -const navigatorLanguageMapping: Record = { - en: 'en-US', - zh: 'zh-CN', - zt: 'zh-TW', - es: 'es-ES', - pt: 'pt-PT', - fr: 'fr-FR', - de: 'de-DE', - it: 'it-IT', - ru: 'ru-RU', - ja: 'ja-JP', - ko: 'ko-KR', - ar: 'ar-SA', - tr: 'tr-TR', - th: 'th-TH', - vi: 'vi-VN', - nl: 'nl-NL', - he: 'iw-IL', - id: 'in-ID', - pl: 'pl-PL', - hi: 'hi-IN', - uk: 'uk-UA', - ms: 'ms-MY', - tl: 'tl-PH', -}; - -/** - * nomarlize navigator.language or user input's locale - * eg: zh -> zh-CN, zh_CN -> zh-CN, zh-cn -> zh-CN - * @param target - */ -function nomarlizeLocale(target: Locale) { - if (navigatorLanguageMapping[target]) { - return navigatorLanguageMapping[target]; - } - - const replaced = target.replace('_', '-'); - const splited = replaced.split('-').slice(0, 2); - splited[1] = splited[1].toUpperCase(); - - return splited.join('-'); -} diff --git a/packages/react-renderer/src/api/app.tsx b/packages/react-renderer/src/api/app.tsx index 360c9fda7..0e1e112f0 100644 --- a/packages/react-renderer/src/api/app.tsx +++ b/packages/react-renderer/src/api/app.tsx @@ -1,22 +1,12 @@ -import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core'; -import { type ComponentType } from 'react'; +import { createRenderer } from '@alilc/lowcode-renderer-core'; import { type Root, createRoot } from 'react-dom/client'; -import { ApplicationView, RendererContext, boosts } from '../app'; - -export interface ReactAppOptions extends AppOptions { - faultComponent?: ComponentType; -} +import { type ReactAppOptions, RendererContext } from './context'; +import { ApplicationView, boosts } from '../app'; export const createApp = async (options: ReactAppOptions) => { return createRenderer(async (context) => { const { schema, boostsManager } = context; - // set config - // if (options.faultComponent) { - // context.config.set('faultComponent', options.faultComponent); - // } - - // extends boosts boostsManager.extend(boosts.toExpose()); let root: Root | undefined; @@ -27,10 +17,11 @@ export const createApp = async (options: ReactAppOptions) => { const defaultId = schema.get('config')?.targetRootID ?? 'app'; const rootElement = normalizeContainer(containerOrId, defaultId); + const contextValue = { ...context, options }; root = createRoot(rootElement); root.render( - + , ); diff --git a/packages/react-renderer/src/api/component.tsx b/packages/react-renderer/src/api/component.tsx index 4ce9e37d6..f829c2901 100644 --- a/packages/react-renderer/src/api/component.tsx +++ b/packages/react-renderer/src/api/component.tsx @@ -1,7 +1,7 @@ import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core'; import { FunctionComponent } from 'react'; import { type LowCodeComponentProps, createComponentBySchema } from '../runtime/schema'; -import { RendererContext } from '../app/context'; +import { RendererContext } from '../api/context'; interface Render { toComponent(): FunctionComponent; @@ -12,10 +12,11 @@ export async function createComponent(options: AppOptions) { const { schema } = context; const LowCodeComponent = createComponentBySchema(schema.get('componentsTree')[0]); + const contextValue = { ...context, options }; function Component(props: LowCodeComponentProps) { return ( - + ); diff --git a/packages/react-renderer/src/api/context.ts b/packages/react-renderer/src/api/context.ts new file mode 100644 index 000000000..ccd666ee7 --- /dev/null +++ b/packages/react-renderer/src/api/context.ts @@ -0,0 +1,14 @@ +import { type ComponentType, createContext, useContext } from 'react'; +import { type AppOptions, type RenderContext } from '@alilc/lowcode-renderer-core'; + +export interface ReactAppOptions extends AppOptions { + faultComponent?: ComponentType; +} + +export const RendererContext = createContext( + undefined!, +); + +RendererContext.displayName = 'RendererContext'; + +export const useRendererContext = () => useContext(RendererContext); diff --git a/packages/react-renderer/src/app/context.ts b/packages/react-renderer/src/app/context.ts deleted file mode 100644 index b987f1a60..000000000 --- a/packages/react-renderer/src/app/context.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createContext, useContext } from 'react'; -import { type RenderContext } from '@alilc/lowcode-renderer-core'; - -export const RendererContext = createContext(undefined!); - -RendererContext.displayName = 'RendererContext'; - -export const useRenderContext = () => useContext(RendererContext); diff --git a/packages/react-renderer/src/app/index.ts b/packages/react-renderer/src/app/index.ts index 83465b5bc..c1e198137 100644 --- a/packages/react-renderer/src/app/index.ts +++ b/packages/react-renderer/src/app/index.ts @@ -1,3 +1,2 @@ -export * from './context'; export * from './boosts'; export * from './view'; diff --git a/packages/react-renderer/src/app/view.tsx b/packages/react-renderer/src/app/view.tsx index f7aff3c45..687101fd4 100644 --- a/packages/react-renderer/src/app/view.tsx +++ b/packages/react-renderer/src/app/view.tsx @@ -1,10 +1,10 @@ -import { useRenderContext } from './context'; +import { useRendererContext } from '../api/context'; import { getComponentByName } from '../runtime/schema'; import { boosts } from './boosts'; export function ApplicationView() { - const renderContext = useRenderContext(); - const { schema } = renderContext; + const rendererContext = useRendererContext(); + const { schema } = rendererContext; const appWrappers = boosts.getAppWrappers(); const Outlet = boosts.getOutlet(); @@ -16,7 +16,7 @@ export function ApplicationView() { if (layoutConfig) { const componentName = layoutConfig.componentName; - const Layout = getComponentByName(componentName, renderContext); + const Layout = getComponentByName(componentName, rendererContext); if (Layout) { const layoutProps: any = layoutConfig.props ?? {}; diff --git a/packages/react-renderer/src/index.ts b/packages/react-renderer/src/index.ts index 8203ff383..fda978f64 100644 --- a/packages/react-renderer/src/index.ts +++ b/packages/react-renderer/src/index.ts @@ -1,7 +1,9 @@ export * from './api/app'; export * from './api/component'; -export { useRenderContext, defineRendererPlugin } from './app'; +export * from './api/context'; +export { defineRendererPlugin } from './app'; export * from './router'; +export { LifecyclePhase } from '@alilc/lowcode-renderer-core'; export type { Spec, ProCodeComponent, LowCodeComponent } from '@alilc/lowcode-shared'; export type { PackageLoader, CodeScope, Plugin } from '@alilc/lowcode-renderer-core'; diff --git a/packages/react-renderer/src/router/index.ts b/packages/react-renderer/src/router/index.ts index 352135a55..1f2f8f6eb 100644 --- a/packages/react-renderer/src/router/index.ts +++ b/packages/react-renderer/src/router/index.ts @@ -1,3 +1,3 @@ export * from './context'; export * from './plugin'; -export type { Router, RouterHistory } from '@alilc/lowcode-renderer-router'; +export type * from '@alilc/lowcode-renderer-router'; diff --git a/packages/react-renderer/src/router/plugin.ts b/packages/react-renderer/src/router/plugin.ts index 7dc3c4b99..ba617ccd0 100644 --- a/packages/react-renderer/src/router/plugin.ts +++ b/packages/react-renderer/src/router/plugin.ts @@ -1,5 +1,4 @@ import { defineRendererPlugin } from '../app/boosts'; -import { LifecyclePhase } from '@alilc/lowcode-renderer-core'; import { createRouter, type RouterOptions } from '@alilc/lowcode-renderer-router'; import { createRouterView } from './routerView'; import { RouteOutlet } from './route'; @@ -13,7 +12,7 @@ const defaultRouterOptions: RouterOptions = { export const routerPlugin = defineRendererPlugin({ name: 'rendererRouter', async setup(context) { - const { whenLifeCylePhaseChange, schema, boosts } = context; + const { schema, boosts } = context; let routerConfig = defaultRouterOptions; @@ -27,17 +26,14 @@ export const routerPlugin = defineRendererPlugin({ } const router = createRouter(routerConfig); - - boosts.codeRuntime.getScope().set('router', router); - boosts.temporaryUse('router', router); - const RouterView = createRouterView(router); boosts.addAppWrapper(RouterView); boosts.setOutlet(RouteOutlet); - whenLifeCylePhaseChange(LifecyclePhase.AfterInitPackageLoad).then(() => { - return router.isReady(); - }); + boosts.codeRuntime.getScope().set('router', router); + boosts.temporaryUse('router', router); + + await router.isReady(); }, }); diff --git a/packages/react-renderer/src/router/route.tsx b/packages/react-renderer/src/router/route.tsx index 03a926dca..615008aef 100644 --- a/packages/react-renderer/src/router/route.tsx +++ b/packages/react-renderer/src/router/route.tsx @@ -1,11 +1,11 @@ import { useMemo } from 'react'; -import { useRenderContext } from '../app/context'; +import { useRendererContext } from '../api/context'; import { OutletProps } from '../app/boosts'; import { useRouteLocation } from './context'; import { createComponentBySchema } from '../runtime/schema'; export function RouteOutlet(props: OutletProps) { - const context = useRenderContext(); + const context = useRendererContext(); const location = useRouteLocation(); const { schema, packageManager } = context; diff --git a/packages/react-renderer/src/runtime/components.tsx b/packages/react-renderer/src/runtime/components.tsx index 57fd1afcd..5aa45d610 100644 --- a/packages/react-renderer/src/runtime/components.tsx +++ b/packages/react-renderer/src/runtime/components.tsx @@ -13,7 +13,7 @@ import { type Spec, } from '@alilc/lowcode-shared'; import { type ComponentType, type ReactInstance, useMemo, createElement } from 'react'; -import { useRenderContext } from '../app/context'; +import { useRendererContext } from '../api/context'; import { useReactiveStore } from './hooks/useReactiveStore'; import { useModel } from './context'; import { getComponentByName } from './schema'; @@ -65,10 +65,10 @@ export function WidgetComponent(props: WidgetRendererProps) { const componentNode = widget.node as NormalizedComponentNode; const { ref, ...componentProps } = componentNode.props; - const renderContext = useRenderContext(); + const rendererContext = useRendererContext(); const Component = useMemo( - () => getComponentByName(componentNode.componentName, renderContext), + () => getComponentByName(componentNode.componentName, rendererContext), [widget], ); @@ -94,11 +94,16 @@ export function WidgetComponent(props: WidgetRendererProps) { return null; } + const finalProps = { + ...otherProps, + ...state.props, + }; + return createElement( Component, { - ...otherProps, - ...state.props, + ...finalProps, + id: finalProps.id ? finalProps.id : undefined, key: widget.key, ref: attachRef, }, diff --git a/packages/react-renderer/src/runtime/dataSource.ts b/packages/react-renderer/src/runtime/dataSource.ts deleted file mode 100644 index daa27c093..000000000 --- a/packages/react-renderer/src/runtime/dataSource.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const dataSourceCreator = () => - ({ - dataSourceMap: {}, - reloadDataSource: () => {}, - }) as any; diff --git a/packages/react-renderer/src/runtime/schema.tsx b/packages/react-renderer/src/runtime/schema.tsx index 9122cbc09..336c11d27 100644 --- a/packages/react-renderer/src/runtime/schema.tsx +++ b/packages/react-renderer/src/runtime/schema.tsx @@ -1,9 +1,8 @@ import { invariant, isLowCodeComponentPackage, type Spec } from '@alilc/lowcode-shared'; import { forwardRef, useRef, useEffect } from 'react'; import { isValidElementType } from 'react-is'; -import { useRenderContext } from '../app/context'; +import { useRendererContext } from '../api/context'; import { reactiveStateFactory } from './reactiveState'; -import { dataSourceCreator } from './dataSource'; import { type ReactComponent, type ReactWidget, createElementByWidget } from './components'; import { ModelContextProvider } from './context'; import { appendExternalStyle } from '../utils/element'; @@ -39,9 +38,7 @@ export function getComponentByName( name: string, { packageManager, boostsManager }: RenderContext, ): ReactComponent { - const componentsRecord = packageManager.getComponentsNameRecord(); - // read cache first - const result = lowCodeComponentsCache.get(name) || componentsRecord[name]; + const result = lowCodeComponentsCache.get(name) || packageManager.getComponent(name); if (isLowCodeComponentPackage(result)) { const { schema, ...metadata } = result; @@ -87,8 +84,8 @@ export function createComponentBySchema( props: LowCodeComponentProps, ref: ForwardedRef, ) { - const renderContext = useRenderContext(); - const { componentTreeModel } = renderContext; + const renderContext = useRendererContext(); + const { options, componentTreeModel } = renderContext; const modelRef = useRef>(); @@ -110,7 +107,7 @@ export function createComponentBySchema( model.initialize({ defaultProps: props, stateCreator: reactiveStateFactory, - dataSourceCreator, + dataSourceCreator: options.dataSourceCreator, }); model.triggerLifeCycle('constructor'); @@ -123,11 +120,6 @@ export function createComponentBySchema( } useEffect(() => { - const scopeValue = model.codeScope.value; - - // init dataSource - scopeValue.reloadDataSource?.(); - // trigger lifeCycles // componentDidMount?.(); model.triggerLifeCycle('componentDidMount'); diff --git a/packages/react-renderer/src/utils/element.ts b/packages/react-renderer/src/utils/element.ts index 103bf871c..f5dbb1ade 100644 --- a/packages/react-renderer/src/utils/element.ts +++ b/packages/react-renderer/src/utils/element.ts @@ -1,7 +1,3 @@ -export const addLeadingSlash = (path: string): string => { - return path.charAt(0) === '/' ? path : `/${path}`; -}; - export interface ExternalElementOptions { id?: string; root?: HTMLElement; diff --git a/packages/renderer-core/__tests__/services/lifeCycle.spec.ts b/packages/renderer-core/__tests__/services/lifeCycle.spec.ts index 139073192..8d068c6f6 100644 --- a/packages/renderer-core/__tests__/services/lifeCycle.spec.ts +++ b/packages/renderer-core/__tests__/services/lifeCycle.spec.ts @@ -14,10 +14,10 @@ describe('LifeCycleService', () => { lifeCycle.when(LifecyclePhase.Ready).finally(() => { result += '2'; }); - lifeCycle.when(LifecyclePhase.AfterInitPackageLoad).then(() => { + lifeCycle.when(LifecyclePhase.Inited).then(() => { result += '3'; }); - lifeCycle.when(LifecyclePhase.AfterInitPackageLoad).finally(() => { + lifeCycle.when(LifecyclePhase.Inited).finally(() => { result += '4'; }); @@ -27,7 +27,7 @@ describe('LifeCycleService', () => { expect(result).toEqual('12'); - lifeCycle.phase = LifecyclePhase.AfterInitPackageLoad; + lifeCycle.phase = LifecyclePhase.Inited; await sleep(); diff --git a/packages/renderer-core/src/main.ts b/packages/renderer-core/src/main.ts index 23642168e..3a7750c04 100644 --- a/packages/renderer-core/src/main.ts +++ b/packages/renderer-core/src/main.ts @@ -30,21 +30,7 @@ export class RendererMain { @IComponentTreeModelService private componentTreeModelService: IComponentTreeModelService, @IBoostsService private boostsService: IBoostsService, @ILifeCycleService private lifeCycleService: ILifeCycleService, - ) { - this.lifeCycleService.when(LifecyclePhase.OptionsResolved).then(async () => { - const renderContext = { - schema: this.schemaService, - packageManager: this.packageManagementService, - boostsManager: this.boostsService, - componentTreeModel: this.componentTreeModelService, - lifeCycle: this.lifeCycleService, - }; - - this.renderObject = await this.adapter(renderContext); - - this.lifeCycleService.phase = LifecyclePhase.Ready; - }); - } + ) {} async main(options: AppOptions, adapter: RenderAdapter) { const { schema, mode, plugins = [] } = options; @@ -58,20 +44,26 @@ export class RendererMain { this.codeRuntimeService.initialize(options.codeRuntime ?? {}); - this.lifeCycleService.phase = LifecyclePhase.OptionsResolved; + await this.lifeCycleService.setPhase(LifecyclePhase.OptionsResolved); + + const renderContext = { + schema: this.schemaService, + packageManager: this.packageManagementService, + boostsManager: this.boostsService, + componentTreeModel: this.componentTreeModelService, + lifeCycle: this.lifeCycleService, + }; - await this.lifeCycleService.when(LifecyclePhase.Ready); + this.renderObject = await this.adapter(renderContext); await this.extensionHostService.registerPlugin(plugins); - + // 先加载插件提供 package loader await this.packageManagementService.loadPackages(this.initOptions.packages ?? []); - this.lifeCycleService.phase = LifecyclePhase.AfterInitPackageLoad; + await this.lifeCycleService.setPhase(LifecyclePhase.Ready); } - async getApp(): Promise> { - await this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad); - + getApp(): RendererApplication { // construct application return Object.freeze>({ // develop use @@ -85,6 +77,9 @@ export class RendererMain { use: (plugin) => { return this.extensionHostService.registerPlugin(plugin); }, + destroy: async () => { + return this.lifeCycleService.setPhase(LifecyclePhase.Destroying); + }, }); } } diff --git a/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts b/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts index 29791f73c..a56485fe4 100644 --- a/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts +++ b/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts @@ -58,11 +58,7 @@ export class CodeRuntimeService implements ICodeRuntimeService { if (!code) return undefined; try { - let result = this.evalCodeFunction(code, scope.value); - - if (typeof result === 'function') { - result = result.bind(scope.value); - } + const result = this.evalCodeFunction(code, scope.value); return result as R; } catch (err) { @@ -105,7 +101,8 @@ export class CodeRuntimeService implements ICodeRuntimeService { node: Spec.JSExpression | Spec.JSFunction, options: ResolveOptions, ) { - const v = this.run(node.value, options.scope || this.codeScope); + const scope = options.scope || this.codeScope; + const v = this.run(node.value, scope) as any; if (typeof v === 'undefined' && node.mock) { return this.resolve(node.mock, options); diff --git a/packages/renderer-core/src/services/code-runtime/codeScope.ts b/packages/renderer-core/src/services/code-runtime/codeScope.ts index c156e74cd..104b2bf1c 100644 --- a/packages/renderer-core/src/services/code-runtime/codeScope.ts +++ b/packages/renderer-core/src/services/code-runtime/codeScope.ts @@ -63,7 +63,7 @@ export class CodeScope implements ICodeScope { private createProxy(): PlainObject { return new Proxy(Object.create(null) as PlainObject, { set: (target, p, newValue) => { - this.set(p as string, newValue, true); + this.set(p as string, newValue); return true; }, get: (_, p) => this.findValue(p) ?? undefined, diff --git a/packages/renderer-core/src/services/extension/extensionHostService.ts b/packages/renderer-core/src/services/extension/extensionHostService.ts index cb55829af..c0010f7fb 100644 --- a/packages/renderer-core/src/services/extension/extensionHostService.ts +++ b/packages/renderer-core/src/services/extension/extensionHostService.ts @@ -3,7 +3,7 @@ import { type Plugin, type PluginContext } from './plugin'; import { IBoostsService } from './boosts'; import { IPackageManagementService } from '../package'; import { ISchemaService } from '../schema'; -import { ILifeCycleService, LifecyclePhase } from '../lifeCycleService'; +import { ILifeCycleService } from '../lifeCycleService'; interface IPluginRuntime extends Plugin { status: 'setup' | 'ready'; @@ -42,8 +42,8 @@ export class ExtensionHostService implements IExtensionHostService { schema: this.schemaService, packageManager: this.packageManagementService, - whenLifeCylePhaseChange: (phase) => { - return this.lifeCycleService.when(phase); + whenLifeCylePhaseChange: (phase, listener) => { + return this.lifeCycleService.when(phase, listener); }, }; } diff --git a/packages/renderer-core/src/services/extension/plugin.ts b/packages/renderer-core/src/services/extension/plugin.ts index 42a693a24..effe95a78 100644 --- a/packages/renderer-core/src/services/extension/plugin.ts +++ b/packages/renderer-core/src/services/extension/plugin.ts @@ -1,6 +1,6 @@ import { type EventEmitter, type IStore, type PlainObject } from '@alilc/lowcode-shared'; import { type IBoosts } from './boosts'; -import { LifecyclePhase } from '../lifeCycleService'; +import { ILifeCycleService } from '../lifeCycleService'; import { type ISchemaService } from '../schema'; import { type IPackageManagementService } from '../package'; @@ -8,12 +8,12 @@ export interface PluginContext { eventEmitter: EventEmitter; globalState: IStore; boosts: IBoosts; - schema: ISchemaService; + schema: Pick; packageManager: IPackageManagementService; /** * 生命周期变更事件 */ - whenLifeCylePhaseChange(phase: LifecyclePhase): Promise; + whenLifeCylePhaseChange: ILifeCycleService['when']; } export interface Plugin { diff --git a/packages/renderer-core/src/services/lifeCycleService.ts b/packages/renderer-core/src/services/lifeCycleService.ts index 8670dfaf3..6378b59a3 100644 --- a/packages/renderer-core/src/services/lifeCycleService.ts +++ b/packages/renderer-core/src/services/lifeCycleService.ts @@ -1,4 +1,4 @@ -import { Provide, createDecorator, Barrier } from '@alilc/lowcode-shared'; +import { Provide, createDecorator, EventEmitter, EventDisposable } from '@alilc/lowcode-shared'; export const enum LifecyclePhase { Starting = 1, @@ -7,7 +7,7 @@ export const enum LifecyclePhase { Ready = 3, - AfterInitPackageLoad = 4, + Destroying = 4, } export interface ILifeCycleService { @@ -16,18 +16,20 @@ export interface ILifeCycleService { */ phase: LifecyclePhase; + setPhase(phase: LifecyclePhase): Promise; + /** * Returns a promise that resolves when a certain lifecycle phase * has started. */ - when(phase: LifecyclePhase): Promise; + when(phase: LifecyclePhase, listener: () => void | Promise): EventDisposable; } export const ILifeCycleService = createDecorator('lifeCycleService'); @Provide(ILifeCycleService) export class LifeCycleService implements ILifeCycleService { - private readonly phaseWhen = new Map(); + private readonly phaseWhen = new EventEmitter(); private _phase = LifecyclePhase.Starting; @@ -35,7 +37,7 @@ export class LifeCycleService implements ILifeCycleService { return this._phase; } - set phase(value: LifecyclePhase) { + async setPhase(value: LifecyclePhase) { if (value < this._phase) { throw new Error('Lifecycle cannot go backwards'); } @@ -44,28 +46,27 @@ export class LifeCycleService implements ILifeCycleService { return; } - // this.logService.trace(`lifecycle: phase changed (value: ${value})`); - this._phase = value; - const barrier = this.phaseWhen.get(this._phase); - if (barrier) { - barrier.open(); - this.phaseWhen.delete(this._phase); - } + await this.phaseWhen.emit(LifecyclePhaseToString(value)); } - async when(phase: LifecyclePhase): Promise { - if (phase <= this._phase) { - return; - } - - let barrier = this.phaseWhen.get(phase); - if (!barrier) { - barrier = new Barrier(); - this.phaseWhen.set(phase, barrier); - } + when(phase: LifecyclePhase, listener: () => void | Promise) { + return this.phaseWhen.on(LifecyclePhaseToString(phase), listener); + } +} - await barrier.wait(); +export function LifecyclePhaseToString(phase: LifecyclePhase): string { + switch (phase) { + case LifecyclePhase.Starting: + return 'Starting'; + case LifecyclePhase.OptionsResolved: + return 'OptionsResolved'; + case LifecyclePhase.Ready: + return 'Ready'; + case LifecyclePhase.Inited: + return 'Inited'; + case LifecyclePhase.Destroying: + return 'Destroying'; } } diff --git a/packages/renderer-core/src/services/model/componentTreeModel.ts b/packages/renderer-core/src/services/model/componentTreeModel.ts index 4d4d68439..dc4810e3a 100644 --- a/packages/renderer-core/src/services/model/componentTreeModel.ts +++ b/packages/renderer-core/src/services/model/componentTreeModel.ts @@ -15,9 +15,8 @@ export interface NormalizedComponentNode extends Spec.ComponentNode { export interface InitializeModelOptions { defaultProps?: PlainObject | undefined; - stateCreator: ModelScopeStateCreator; - dataSourceCreator: ModelScopeDataSourceCreator; + dataSourceCreator?: ModelScopeDataSourceCreator; } /** @@ -60,14 +59,6 @@ export interface IComponentTreeModel { export type ModelScopeStateCreator = (initalState: PlainObject) => Spec.InstanceStateApi; export type ModelScopeDataSourceCreator = (...args: any[]) => Spec.InstanceDataSourceApi; -const defaultDataSourceSchema: Spec.ComponentDataSource = { - list: [], - dataHandler: { - type: 'JSFunction', - value: '() => {}', - }, -}; - export interface ComponentTreeModelOptions { id?: string; metadata?: PlainObject; @@ -108,7 +99,7 @@ export class ComponentTreeModel state = {}, defaultProps: defaultSchemaProps, props = {}, - dataSource = defaultDataSourceSchema, + dataSource, methods = {}, } = this.componentsTree; @@ -120,16 +111,22 @@ export class ComponentTreeModel }, }); - const initalState = this.codeRuntime.resolve(state, { scope: this.codeScope }); const initalProps = this.codeRuntime.resolve(props, { scope: this.codeScope }); + this.codeScope.setValue({ props: { ...defaultProps, ...initalProps } }); + const initalState = this.codeRuntime.resolve(state, { scope: this.codeScope }); const stateApi = stateCreator(initalState); - const dataSourceApi = dataSourceCreator(dataSource, stateApi); + this.codeScope.setValue(stateApi); + + let dataSourceApi: Spec.InstanceDataSourceApi | undefined; + if (dataSource && dataSourceCreator) { + const dataSourceProps = this.codeRuntime.resolve(dataSource, { scope: this.codeScope }); + dataSourceApi = dataSourceCreator(dataSourceProps, stateApi); + } this.codeScope.setValue( Object.assign( { - props: { ...defaultProps, ...initalProps }, $: (ref: string) => { const insArr = this.instanceMap.get(ref); if (!insArr) return undefined; @@ -139,7 +136,6 @@ export class ComponentTreeModel return this.instanceMap.get(ref) ?? []; }, }, - stateApi, dataSourceApi, ), ); diff --git a/packages/renderer-core/src/services/package/managementService.ts b/packages/renderer-core/src/services/package/managementService.ts index 42695244a..5ab95bbed 100644 --- a/packages/renderer-core/src/services/package/managementService.ts +++ b/packages/renderer-core/src/services/package/managementService.ts @@ -10,7 +10,7 @@ import { import { get as lodashGet } from 'lodash-es'; import { PackageLoader } from './loader'; import { ISchemaService } from '../schema'; -import { ILifeCycleService, LifecyclePhase } from '../lifeCycleService'; +import { ILifeCycleService } from '../lifeCycleService'; export interface NormalizedPackage { id: string; @@ -33,16 +33,13 @@ export interface IPackageManagementService { setLibraryByPackageName(packageName: string, library: any): void; - getLibraryByComponentMap(componentMap: Spec.ComponentMap): any; + getLibraryByComponentMap( + componentMap: Spec.ComponentMap, + ): { key: string; value: any } | undefined; /** 解析组件映射 */ resolveComponentMaps(componentMaps: Spec.ComponentMap[]): void; - /** 获取组件映射对象,key = componentName value = component */ - getComponentsNameRecord( - componentMaps?: Spec.ComponentMap[], - ): Record; - /** 通过组件名获取对应的组件 */ getComponent(componentName: string): C | LowCodeComponent | undefined; /** 注册组件 */ @@ -72,8 +69,7 @@ export class PackageManagementService implements IPackageManagementService { @ISchemaService private schemaService: ISchemaService, @ILifeCycleService private lifeCycleService: ILifeCycleService, ) { - this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad).then(() => { - const componentsMaps = this.schemaService.get('componentsMap'); + this.schemaService.onChange('componentsMap', (componentsMaps) => { this.resolveComponentMaps(componentsMaps); }); } @@ -109,19 +105,23 @@ export class PackageManagementService implements IPackageManagementService { this.packageStore.set(packageName, library); } - getLibraryByComponentMap(componentMap: Spec.ComponentMap) { - if (this.packageStore.has(componentMap.package!)) { + getLibraryByComponentMap( + componentMap: Spec.ComponentMap, + ): { key: string; value: any } | undefined { + if (!componentMap.componentName && !componentMap.exportName) return; + + if (this.packageStore.has(componentMap.package)) { const library = this.packageStore.get(componentMap.package!); // export { exportName } from xxx exportName === global.libraryName.exportName // export exportName from xxx exportName === global.libraryName.default || global.libraryName - // export { exportName as componentName } from package - // if exportName == null exportName === componentName; // const componentName = exportName.subName, if exportName empty subName donot use const paths = componentMap.exportName && componentMap.subName ? componentMap.subName.split('.') : []; + + // if exportName === nil, exportName === componentName; const exportName = componentMap.exportName ?? componentMap.componentName; - if (componentMap.destructuring) { + if (exportName && componentMap.destructuring) { paths.unshift(exportName); } @@ -130,10 +130,12 @@ export class PackageManagementService implements IPackageManagementService { result = result[path] || result; } - return result; + // export { exportName as componentName } from package + return { + key: componentMap.componentName ?? componentMap.exportName!, + value: result, + }; } - - return undefined; } resolveComponentMaps(componentMaps: Spec.ComponentMap[]) { @@ -146,22 +148,15 @@ export class PackageManagementService implements IPackageManagementService { } } else { const result = this.getLibraryByComponentMap(map); - if (map.componentName && result) this.componentsRecord[map.componentName] = result; + if (result) { + this.componentsRecord[result.key] = result.value; + } } } } - getComponentsNameRecord(componentMaps?: Spec.ComponentMap[]) { - if (componentMaps) { - const newMaps = componentMaps.filter((item) => !this.componentsRecord[item.componentName]); - this.resolveComponentMaps(newMaps); - } - - return { ...this.componentsRecord }; - } - getComponent(componentName: string) { - return this.componentsRecord[componentName]; + return lodashGet(this.componentsRecord, componentName); } registerComponentByName(componentName: string, Component: unknown) { diff --git a/packages/renderer-core/src/services/runtimeIntlService.ts b/packages/renderer-core/src/services/runtimeIntlService.ts index 1aa532c23..90dc9724f 100644 --- a/packages/renderer-core/src/services/runtimeIntlService.ts +++ b/packages/renderer-core/src/services/runtimeIntlService.ts @@ -42,7 +42,9 @@ export class RuntimeIntlService implements IRuntimeIntlService { @ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService, @ISchemaService private schemaService: ISchemaService, ) { - this.lifeCycleService.when(LifecyclePhase.OptionsResolved).then(() => { + this.injectScope(); + + this.lifeCycleService.when(LifecyclePhase.OptionsResolved, () => { const config = this.schemaService.get('config'); const i18nTranslations = this.schemaService.get('i18n'); @@ -55,10 +57,6 @@ export class RuntimeIntlService implements IRuntimeIntlService { }); } }); - - this.lifeCycleService.when(LifecyclePhase.Ready).then(() => { - this.toExpose(); - }); } t(descriptor: MessageDescriptor): string { @@ -85,7 +83,7 @@ export class RuntimeIntlService implements IRuntimeIntlService { this.intl.addTranslations(locale, translations); } - private toExpose(): void { + private injectScope(): void { const exposed: Spec.IntlApi = { i18n: (key, params) => { return this.t({ key, params }); diff --git a/packages/renderer-core/src/services/runtimeUtilService.ts b/packages/renderer-core/src/services/runtimeUtilService.ts index 69f4422d7..9b5fcfad5 100644 --- a/packages/renderer-core/src/services/runtimeUtilService.ts +++ b/packages/renderer-core/src/services/runtimeUtilService.ts @@ -8,12 +8,11 @@ import { import { isPlainObject } from 'lodash-es'; import { IPackageManagementService } from './package'; import { ICodeRuntimeService } from './code-runtime'; -import { ILifeCycleService, LifecyclePhase } from './lifeCycleService'; import { ISchemaService } from './schema'; export interface IRuntimeUtilService { - add(utilItem: Spec.Util): void; - add(name: string, target: AnyFunction | PlainObject): void; + add(utilItem: Spec.Util, force?: boolean): void; + add(name: string, target: AnyFunction | PlainObject, force?: boolean): void; remove(name: string): void; } @@ -27,38 +26,61 @@ export class RuntimeUtilService implements IRuntimeUtilService { constructor( @ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService, @IPackageManagementService private packageManagementService: IPackageManagementService, - @ILifeCycleService private lifeCycleService: ILifeCycleService, @ISchemaService private schemaService: ISchemaService, ) { - this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad).then(() => { - const utils = this.schemaService.get('utils') ?? []; + this.injectScope(); + + this.schemaService.onChange('utils', (utils = []) => { for (const util of utils) { this.add(util); } - this.toExpose(); }); } - add(utilItem: Spec.Util): void; - add(name: string, fn: AnyFunction | PlainObject): void; - add(name: Spec.Util | string, fn?: AnyFunction | PlainObject): void { - if (typeof name === 'string') { - if (fn) { - if (isPlainObject(fn)) { - if ((fn as PlainObject).destructuring) { - for (const key of Object.keys(fn)) { - this.add(key, (fn as PlainObject)[key]); - } - } else { - this.utilsMap.set(name, fn); - } - } else if (typeof fn === 'function') { - this.utilsMap.set(name, fn); + add(utilItem: Spec.Util, force?: boolean): void; + add(name: string, fn: AnyFunction | PlainObject, force?: boolean): void; + add(util: Spec.Util | string, fn?: AnyFunction | PlainObject | boolean, force?: boolean): void { + let name: string; + let utilObj: AnyFunction | PlainObject | Spec.Util; + + if (typeof util === 'string') { + if (!fn) return; + + name = util; + utilObj = fn as AnyFunction | PlainObject; + } else { + if (!util) return; + + name = util.name; + utilObj = util; + force = fn as boolean; + } + + this.addUtilByName(name, utilObj, force); + } + + private addUtilByName( + name: string, + fn: AnyFunction | PlainObject | Spec.Util, + force?: boolean, + ): void { + if (this.utilsMap.has(name) && !force) return; + + if (isPlainObject(fn)) { + if ((fn as Spec.Util).type === 'function' || (fn as Spec.Util).type === 'npm') { + const utilFn = this.parseUtil(fn as Spec.Util); + if (utilFn) { + this.addUtilByName(utilFn.key, utilFn.value, force); + } + } else if ((fn as PlainObject).destructuring) { + for (const key of Object.keys(fn)) { + this.addUtilByName(key, (fn as PlainObject)[key], force); } + } else { + this.utilsMap.set(name, fn); } - } else { - const util = this.parseUtil(name); - if (util) this.add(name.name, util); + } else if (typeof fn === 'function') { + this.utilsMap.set(name, fn); } } @@ -69,13 +91,16 @@ export class RuntimeUtilService implements IRuntimeUtilService { private parseUtil(utilItem: Spec.Util) { if (utilItem.type === 'function') { const { content } = utilItem; - return this.codeRuntimeService.run(content.value); + return { + key: utilItem.name, + value: this.codeRuntimeService.run(content.value), + }; } else { return this.packageManagementService.getLibraryByComponentMap(utilItem.content); } } - private toExpose(): void { + private injectScope(): void { const exposed = new Proxy(Object.create(null), { get: (_, p: string) => { return this.utilsMap.get(p); diff --git a/packages/renderer-core/src/services/schema/schemaService.ts b/packages/renderer-core/src/services/schema/schemaService.ts index ee2dd5784..4a59dd1a1 100644 --- a/packages/renderer-core/src/services/schema/schemaService.ts +++ b/packages/renderer-core/src/services/schema/schemaService.ts @@ -4,6 +4,8 @@ import { Provide, type IStore, KeyValueStore, + EventEmitter, + type EventDisposable, } from '@alilc/lowcode-shared'; import { isObject } from 'lodash-es'; import { schemaValidation } from './validation'; @@ -19,7 +21,12 @@ export interface ISchemaService { get(key: K): NormalizedSchema[K]; - set(key: K, value: NormalizedSchema[K]): void; + set(key: K, value: NormalizedSchema[K]): Promise; + + onChange( + key: K, + listener: (v: NormalizedSchema[K]) => void, + ): EventDisposable; } export const ISchemaService = createDecorator('schemaService'); @@ -33,13 +40,18 @@ export class SchemaService implements ISchemaService { setterValidation: schemaValidation, }); + private notifyEmiiter = new EventEmitter(); + constructor( @ILifeCycleService private lifeCycleService: ILifeCycleService, @ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService, ) { - this.lifeCycleService.when(LifecyclePhase.Ready).then(() => { - const constants = this.get('constants') ?? {}; - this.codeRuntimeService.getScope().set('constants', constants); + this.onChange('constants', (value = {}) => { + this.codeRuntimeService.getScope().set('constants', value); + }); + + this.lifeCycleService.when(LifecyclePhase.Destroying, () => { + this.notifyEmiiter.removeAll(); }); } @@ -54,11 +66,21 @@ export class SchemaService implements ISchemaService { }); } - set(key: K, value: NormalizedSchema[K]): void { - this.store.set(key, value); + async set(key: K, value: NormalizedSchema[K]): Promise { + if (value !== this.get(key)) { + this.store.set(key, value); + await this.notifyEmiiter.emit(key, value); + } } get(key: K): NormalizedSchema[K] { return this.store.get(key) as NormalizedSchema[K]; } + + onChange( + key: K, + listener: (v: NormalizedSchema[K]) => void | Promise, + ): EventDisposable { + return this.notifyEmiiter.on(key, listener); + } } diff --git a/packages/renderer-core/src/types.ts b/packages/renderer-core/src/types.ts index eac89badb..769dd5e55 100644 --- a/packages/renderer-core/src/types.ts +++ b/packages/renderer-core/src/types.ts @@ -3,21 +3,24 @@ import { type Plugin } from './services/extension'; import { type ISchemaService } from './services/schema'; import { type IPackageManagementService } from './services/package'; import { type CodeRuntimeInitializeOptions } from './services/code-runtime'; +import { type ModelScopeDataSourceCreator } from './services/model'; export interface AppOptions { schema: Spec.Project; packages?: Spec.Package[]; plugins?: Plugin[]; - /** * 运行模式 */ mode?: 'development' | 'production'; - /** * code runtime 设置选项 */ codeRuntime?: CodeRuntimeInitializeOptions; + /** + * 数据源创建工厂函数 + */ + dataSourceCreator?: ModelScopeDataSourceCreator; } export type RendererApplication = { @@ -28,4 +31,6 @@ export type RendererApplication = { readonly packageManager: IPackageManagementService; use(plugin: Plugin): Promise; + + destroy(): Promise; } & Render; diff --git a/packages/renderer-router/src/router.ts b/packages/renderer-router/src/router.ts index d682f91fb..2947eeb6f 100644 --- a/packages/renderer-router/src/router.ts +++ b/packages/renderer-router/src/router.ts @@ -8,7 +8,7 @@ import { } from './history'; import { createRouterMatcher } from './matcher'; import { type PathParserOptions, type PathParams } from './utils/path-parser'; -import { parseURL, stringifyURL } from './utils/url'; +import { mergeSearchParams, parseURL, stringifyURL } from './utils/url'; import { isSameRouteLocation } from './utils/helper'; import type { RouteRecord, @@ -77,11 +77,11 @@ export function createRouter(options: RouterOptions): Router { function resolve( rawLocation: RawRouteLocation, - currentLocation?: RouteLocationNormalized, + current?: RouteLocationNormalized, ): RouteLocationNormalized & { href: string; } { - currentLocation = Object.assign({}, currentLocation || currentLocation); + currentLocation = Object.assign({}, current || currentLocation); if (typeof rawLocation === 'string') { const locationNormalized = parseURL(rawLocation); @@ -89,7 +89,10 @@ export function createRouter(options: RouterOptions): Router { const href = routerHistory.createHref(locationNormalized.fullPath); return Object.assign(locationNormalized, matchedRoute, { - searchParams: locationNormalized.searchParams, + searchParams: mergeSearchParams( + currentLocation.searchParams, + locationNormalized.searchParams, + ), hash: decodeURIComponent(locationNormalized.hash), redirectedFrom: undefined, href, diff --git a/packages/renderer-router/src/utils/url.ts b/packages/renderer-router/src/utils/url.ts index e4a4b6e8c..1fb5837b4 100644 --- a/packages/renderer-router/src/utils/url.ts +++ b/packages/renderer-router/src/utils/url.ts @@ -45,3 +45,14 @@ export function stringifyURL(location: { const searchStr = location.searchParams ? location.searchParams.toString() : ''; return location.path + (searchStr && '?') + searchStr + (location.hash || ''); } + +export function mergeSearchParams( + a: URLSearchParams | undefined, + b: URLSearchParams | undefined, +): URLSearchParams { + if (!a && !b) return new URLSearchParams(); + if (!a) return b!; + if (!b) return a; + + return new URLSearchParams([...Array.from(a.entries()), ...Array.from(b.entries())]); +} diff --git a/packages/shared/src/types/specs/lowcode-spec.ts b/packages/shared/src/types/specs/lowcode-spec.ts index 03f69a8e7..96c22f957 100644 --- a/packages/shared/src/types/specs/lowcode-spec.ts +++ b/packages/shared/src/types/specs/lowcode-spec.ts @@ -44,7 +44,7 @@ export interface Project { /** * 当前应用元数据信息 */ - meta?: Record; + meta?: Record; /** * 当前应用的公共数据源 * @deprecated @@ -92,13 +92,13 @@ export interface ProjectConfig { */ export interface ComponentMap { /** - * 协议中的组件名,唯一性,对应包导出的组件名,是一个有效的 JS 标识符,而且是大写字母打头 + * 协议中的组件名,对应包导出的组件名,是一个有效的 JS 标识符 */ - componentName: string; + componentName?: string; /** - * npm 公域的 package name + * npm 公域的 package name,唯一性 */ - package?: string; + package: string; /** * package version */