From a1cc2abaf180ddf458ce9fa728dd848e56d0fd76 Mon Sep 17 00:00:00 2001 From: hemengke <23536175@qq.com> Date: Thu, 25 Apr 2024 11:53:55 +0800 Subject: [PATCH] chore: release --- packages/istanbul-widget/CHANGELOG.md | 13 ++++ packages/istanbul-widget/README.md | 64 +++++++++++++++++++ packages/istanbul-widget/dev.tsx | 26 ++++++-- packages/istanbul-widget/package.json | 5 +- packages/istanbul-widget/src/core/Context.ts | 3 +- .../src/core/IstanbulWidget.tsx | 9 ++- packages/istanbul-widget/src/core/core.ts | 56 ++++++++++------ .../src/core/options.interface.ts | 7 ++ .../src/core/plugin/IstanbulWidgetPlugin.ts | 57 +++++++++++++++-- .../core/plugin/IstanbulWidgetReactPlugin.ts | 47 ++++---------- packages/istanbul-widget/src/utils/query.ts | 2 +- .../vite-plugin-istanbul-widget/CHANGELOG.md | 11 ++++ .../vite-plugin-istanbul-widget/package.json | 2 +- pnpm-lock.yaml | 22 +++++-- 14 files changed, 241 insertions(+), 83 deletions(-) diff --git a/packages/istanbul-widget/CHANGELOG.md b/packages/istanbul-widget/CHANGELOG.md index 84215d6..1dcad31 100644 --- a/packages/istanbul-widget/CHANGELOG.md +++ b/packages/istanbul-widget/CHANGELOG.md @@ -1,5 +1,18 @@ # istanbul-widget +## 1.4.0 + +### Minor Changes + +- feat + + - debug mode + - html+dom plugin + +- refactor + + - istanbulWidget.event.on ===> istanbulWidget.on + ## 1.3.3 ### Patch Changes diff --git a/packages/istanbul-widget/README.md b/packages/istanbul-widget/README.md index d1db6cc..39c3c0b 100644 --- a/packages/istanbul-widget/README.md +++ b/packages/istanbul-widget/README.md @@ -119,9 +119,73 @@ interface IstanbulWidgetOptions { * 插件顺序 */ pluginOrder?: (PluginName | string)[] + /** + * 打印调试信息 + * @default false + */ + debug?: boolean } ``` +## 编写插件 + +支持 react插件 和 原生html+dom插件 + +```tsx +import { IstanbulWidget } from 'istanbul-widget' +import { Button } from 'istanbul-widget/components' + +function ReactPlugin() { + return +} + +// 自定义react插件 +const reactPlugin = new IstanbulWidget.IstanbulWidgetReactPlugin('react_plugin', 'React Plugin', ReactPlugin) + +reactPlugin.on('init', () => { + console.log('react plugin inited') +}) + +// 自定义html插件 +const htmlEl = document.createElement('div') +htmlEl.innerHTML = 'this is html plugin' +const htmlPlugin = new IstanbulWidget.IstanbulWidgetPlugin('html_plugin', 'HTML Plugin', htmlEl) + +htmlPlugin.on('init', () => { + console.log('html plugin inited') +}) + +const istanbulWidget = new IstanbulWidget({ + defaultPosition: { + x: 0, + y: 100, + }, + plugin: { + // 上报按钮 + report: { + onReport(coverage) { + console.log('上报', coverage) + throw new Error('上报失败') + }, + }, + // 设置插件 + setting: { + requireReporter: true, + }, + buttonGroup: [ + { + text: '额外按钮 - 1', + onClick() { + console.log('1') + }, + }, + ], + }, +}) + +istanbulWidget.addPlugin(reactPlugin) +istanbulWidget.addPlugin(htmlPlugin) +``` ## 截图 diff --git a/packages/istanbul-widget/dev.tsx b/packages/istanbul-widget/dev.tsx index 71553b0..72fa6fc 100644 --- a/packages/istanbul-widget/dev.tsx +++ b/packages/istanbul-widget/dev.tsx @@ -1,15 +1,24 @@ import { Button } from './src/components/ui' import { IstanbulWidget } from './src/istanbul-widget' -function MyPlugin() { - return +function ReactPlugin() { + return } -// 自定义插件 -const myPlugin = new IstanbulWidget.IstanbulWidgetReactPlugin('my_plugin', 'My Plugin', MyPlugin) +// 自定义react插件 +const reactPlugin = new IstanbulWidget.IstanbulWidgetReactPlugin('react_plugin', 'React Plugin', ReactPlugin) -myPlugin.event.on('init', () => { - console.log('my plugin inited') +reactPlugin.on('init', () => { + console.log('react plugin inited') +}) + +// 自定义html插件 +const htmlEl = document.createElement('div') +htmlEl.innerHTML = 'this is html plugin' +const htmlPlugin = new IstanbulWidget.IstanbulWidgetPlugin('html_plugin', 'HTML Plugin', htmlEl) + +htmlPlugin.on('init', () => { + console.log('html plugin inited') }) const istanbulWidget = new IstanbulWidget({ @@ -44,6 +53,9 @@ const istanbulWidget = new IstanbulWidget({ }, ], }, + pluginOrder: ['report', 'react_plugin', 'html_plugin', 'buttonGroup', 'setting'], + debug: true, }) -istanbulWidget.addPlugin(myPlugin) +istanbulWidget.addPlugin(reactPlugin) +istanbulWidget.addPlugin(htmlPlugin) diff --git a/packages/istanbul-widget/package.json b/packages/istanbul-widget/package.json index a51c5b1..a078efe 100644 --- a/packages/istanbul-widget/package.json +++ b/packages/istanbul-widget/package.json @@ -1,6 +1,6 @@ { "name": "istanbul-widget", - "version": "1.3.3", + "version": "1.4.0", "type": "module", "files": [ "**" @@ -47,10 +47,11 @@ "@radix-ui/react-toast": "^1.1.5", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "consola": "^3.2.3", "context-state": "^2.3.1", + "eventemitter3": "^5.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "strict-event-emitter": "^0.5.1", "tailwind-merge": "^2.2.2", "tailwind-variants": "^0.2.1", "tailwindcss-animate": "^1.0.7" diff --git a/packages/istanbul-widget/src/core/Context.ts b/packages/istanbul-widget/src/core/Context.ts index 8b3df77..8d1bce2 100644 --- a/packages/istanbul-widget/src/core/Context.ts +++ b/packages/istanbul-widget/src/core/Context.ts @@ -1,6 +1,7 @@ import { useDebounceFn, useLatest, useSetState } from '@minko-fe/react-hook' import { createContainer } from 'context-state' import { useToast } from '@/components/ui' +import { IstanbulWidget } from './core' import { type IstanbulWidgetOptions, type PluginName, type PluginType, type ReportParams } from './options.interface' export type InitialWidgetProps = IstanbulWidgetOptions & { @@ -59,7 +60,7 @@ function useContext(initialValues: InitialWidgetProps) { description: '上报失败,请打开控制台查看原因', variant: 'destructive', }) - console.error('[istanbul-widget]:', e) + IstanbulWidget.logger.error('[istanbul-widget]:', e) } finally { // after report await Promise.all(reportActions.after.map(async (action) => await action.fn(latestParams))) diff --git a/packages/istanbul-widget/src/core/IstanbulWidget.tsx b/packages/istanbul-widget/src/core/IstanbulWidget.tsx index e2704dc..240cfbc 100644 --- a/packages/istanbul-widget/src/core/IstanbulWidget.tsx +++ b/packages/istanbul-widget/src/core/IstanbulWidget.tsx @@ -78,7 +78,7 @@ export default function IstanbulWidget() { style={{ backgroundColor: 'rgba(0, 0, 0, 0.25)', }} - id='__iw-icon' + id={`${ISTANBUL_WIDGET_ID}__icon`} >
@@ -88,12 +88,15 @@ export default function IstanbulWidget() {
{Object.entries(pluginList).map(([_, plugin]) => { return ( -
+
el && plugin.htmlElement && el.appendChild(plugin.htmlElement)} + /> ) })} diff --git a/packages/istanbul-widget/src/core/core.ts b/packages/istanbul-widget/src/core/core.ts index 41af020..b34ca7d 100644 --- a/packages/istanbul-widget/src/core/core.ts +++ b/packages/istanbul-widget/src/core/core.ts @@ -1,11 +1,12 @@ import { deepMerge, isArray, isFunction, isObject, set } from '@minko-fe/lodash-pro' +import { type ConsolaInstance, LogLevels, createConsola } from 'consola' import { ButtonGroupPlugin } from '@/plugins/button-group/ButtonGroupPlugin' import { ReportPlugin } from '@/plugins/report/ReportPlugin' import { SettingPlugin } from '@/plugins/setting/SettingPlugin' import { ISTANBUL_WIDGET_ID } from '@/utils/const' import { $ } from '@/utils/query' import Context from './Context' -import { type IstanbulWidgetOptions, type PluginName } from './options.interface' +import { type IstanbulWidgetOptions, type PluginName, type PluginType } from './options.interface' import { IstanbulWidgetPlugin } from './plugin/IstanbulWidgetPlugin' import { IstanbulWidgetReactPlugin } from './plugin/IstanbulWidgetReactPlugin' import { type CompInstance, render } from './render' @@ -41,9 +42,16 @@ export class IstanbulWidget { public static Context: typeof Context + public static logger: ConsolaInstance + constructor(opts: IstanbulWidgetOptions) { + IstanbulWidget.logger = createConsola({ + level: opts.debug ? LogLevels.debug : LogLevels.box, + fancy: true, + }) + if (!!IstanbulWidget.instance && IstanbulWidget.instance instanceof IstanbulWidget) { - console.debug('[istanbul-widget] IstanbulWidget is already exists.') + IstanbulWidget.logger.debug('[istanbul-widget] IstanbulWidget is already exists.') return IstanbulWidget.instance } @@ -76,16 +84,16 @@ export class IstanbulWidget { _onload() } } else { - let _timer + let _timer: number const _pollingDocument = () => { if (!!document && document.readyState === 'complete') { _timer && clearTimeout(_timer) _onload() } else { - _timer = setTimeout(_pollingDocument, 1) + _timer = window.setTimeout(_pollingDocument, 1) } } - _timer = setTimeout(_pollingDocument, 1) + _timer = window.setTimeout(_pollingDocument, 1) } } @@ -123,7 +131,7 @@ export class IstanbulWidget { */ private _addBuiltInPlugins() { // add default report plugin - this.addPlugin(new ReportPlugin(`${ISTANBUL_WIDGET_ID}_report__`, '上报插件')) + this.addPlugin(new ReportPlugin('report', '上报插件')) // add other built-in plugins according to user's config const list = this.option.defaultPlugins @@ -149,9 +157,9 @@ export class IstanbulWidget { for (let i = 0; i < list.length; i++) { const pluginConf = plugins[list[i]] if (pluginConf) { - this.addPlugin(new pluginConf.proto(`${ISTANBUL_WIDGET_ID}_${list[i]}__`, pluginConf.name, pluginConf.props)) + this.addPlugin(new pluginConf.proto(list[i], pluginConf.name, pluginConf.props)) } else { - console.debug('[istanbul-widget] Unrecognized default plugin ID:', list[i]) + IstanbulWidget.logger.debug('[istanbul-widget] Unrecognized default plugin ID:', list[i]) } } } @@ -163,7 +171,7 @@ export class IstanbulWidget { public addPlugin(plugin: IstanbulWidgetPlugin) { // ignore this plugin if it has already been installed if (this.pluginList[plugin.id] !== undefined) { - console.debug(`[istanbul-widget] Plugin \`${plugin.id}\` has already been added.`) + IstanbulWidget.logger.debug(`[istanbul-widget] Plugin \`${plugin.id}\` has already been added.`) return false } this.pluginList[plugin.id] = plugin @@ -183,7 +191,8 @@ export class IstanbulWidget { set(this.compInstance.pluginList, plugin.id, { id: plugin.id, name: plugin.name, - }) + domID: plugin.domID, + } as PluginType) this.compInstance.pluginList = this._reorderPluginList(this.compInstance.pluginList) @@ -194,12 +203,17 @@ export class IstanbulWidget { }) .then(() => { // start init - plugin.event.emit('init') + plugin.emit('init') // render - plugin.event.emit('render', () => {}) + plugin.emit('render', ({ htmlElement }) => { + if (htmlElement) { + this.compInstance.pluginList[plugin.id].htmlElement = htmlElement + this.compInstance.update({ pluginList: this.compInstance.pluginList }) + } + }) // end init plugin.isReady = true - plugin.event.emit('ready') + plugin.emit('ready') }) } @@ -211,17 +225,19 @@ export class IstanbulWidget { this._initPlugin(this.pluginList[id]) } - this.triggerEvent('ready') + this.emitEvent('ready') - if (process.env.NODE_ENV === 'development') { - console.log(`[istanbul-widget]: v${this.version}`) - } + IstanbulWidget.logger.debug(`[istanbul-widget]: v${this.version}`) } /** - * Trigger a `istanbulWidget.option` event. + * emit a `istanbulWidget.option` event + * @example + * ```js + * istanbulWidget.emitEvent('ready') // will emit `istanbulWidget.option.onReady()` + * ``` */ - public triggerEvent(eventName: string, param?: any) { + public emitEvent(eventName: string, param?: any) { eventName = `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}` if (isFunction(this.option[eventName])) { setTimeout(() => { @@ -275,7 +291,7 @@ export class IstanbulWidget { public static set instance(value: IstanbulWidget | undefined) { if (value !== undefined && !(value instanceof IstanbulWidget)) { - console.debug( + IstanbulWidget.logger.debug( '[istanbul-widget] Cannot set `IstanbulWidget.instance` because the value is not the instance of IstanbulWidget.', ) return diff --git a/packages/istanbul-widget/src/core/options.interface.ts b/packages/istanbul-widget/src/core/options.interface.ts index 2b1b644..a418c16 100644 --- a/packages/istanbul-widget/src/core/options.interface.ts +++ b/packages/istanbul-widget/src/core/options.interface.ts @@ -70,6 +70,11 @@ export interface IstanbulWidgetOptions { * 插件顺序 */ pluginOrder?: (PluginName | string)[] + /** + * 打印调试信息 + * @default false + */ + debug?: boolean } export type ReportParams = { @@ -145,6 +150,8 @@ export type Position = { export type PluginType = { id: string name: string + domID: string + htmlElement?: HTMLElement } export type PluginName = Exclude[number] diff --git a/packages/istanbul-widget/src/core/plugin/IstanbulWidgetPlugin.ts b/packages/istanbul-widget/src/core/plugin/IstanbulWidgetPlugin.ts index f330c4e..f58c206 100644 --- a/packages/istanbul-widget/src/core/plugin/IstanbulWidgetPlugin.ts +++ b/packages/istanbul-widget/src/core/plugin/IstanbulWidgetPlugin.ts @@ -1,12 +1,12 @@ -import type ReactDOM from 'react-dom/client' -import { uniqueId } from '@minko-fe/lodash-pro' -import { Emitter } from 'strict-event-emitter' +import { isFunction, uniqueId } from '@minko-fe/lodash-pro' +import EventEmitter from 'eventemitter3' +import { ISTANBUL_WIDGET_ID } from '@/utils/const' import { type IstanbulWidget } from '../core' type Events = { init: [] ready: [] - render: [callback?: (res: { root: ReactDOM.Root; domNode: HTMLDivElement }) => void] + render: [callback: (res: { htmlElement: HTMLElement | undefined }) => void] } export class IstanbulWidgetPlugin { @@ -15,12 +15,21 @@ export class IstanbulWidgetPlugin { protected _name!: string protected _istanbulWidget!: IstanbulWidget - event: Emitter = new Emitter() + public domID: string - constructor(id: string, name: string) { + private _event: EventEmitter = new EventEmitter() + + constructor( + id: string, + name: string, + protected htmlElement?: HTMLElement, + ) { this.id = id + this.domID = `${ISTANBUL_WIDGET_ID}__plugin--${id}` this.name = name this.isReady = false + + this.registerEvents() } get id() { @@ -33,7 +42,7 @@ export class IstanbulWidgetPlugin { } else if (!value) { throw '[istanbul-widget] Plugin ID cannot be empty.' } - this._id = value.toLowerCase() + this._id = value } get name() { @@ -58,6 +67,40 @@ export class IstanbulWidgetPlugin { this._istanbulWidget = value } + /** + * Add a listener for a given event + */ + public on = this._event.on.bind(this._event) + /** + * Remove an event listener + */ + public off = this._event.off.bind(this._event) + /** + * Calls each of the listeners registered for a given event + */ + public emit = this._event.emit.bind(this._event) + + protected registerEvents() { + const properties = Object.getOwnPropertyNames(IstanbulWidgetPlugin.prototype) + + const onMethods = properties.filter((name) => name.match(/^on[A-Z].*/) && isFunction(this[name])) + onMethods.forEach((methodName) => { + this[methodName].call(this) + }) + } + + public onReady() { + this.on('ready', () => { + this.isReady = true + }) + } + + public onRender() { + this.on('render', (callback) => { + callback({ htmlElement: this.htmlElement }) + }) + } + protected getUniqueID(prefix: string = '') { return uniqueId(prefix) } diff --git a/packages/istanbul-widget/src/core/plugin/IstanbulWidgetReactPlugin.ts b/packages/istanbul-widget/src/core/plugin/IstanbulWidgetReactPlugin.ts index 2423611..364f4d7 100644 --- a/packages/istanbul-widget/src/core/plugin/IstanbulWidgetReactPlugin.ts +++ b/packages/istanbul-widget/src/core/plugin/IstanbulWidgetReactPlugin.ts @@ -1,13 +1,11 @@ -import { isFunction } from '@minko-fe/lodash-pro' import React from 'react' import ReactDOM from 'react-dom/client' import { $ } from '@/utils/query' +import { IstanbulWidget } from '../core' +import { type PluginType } from '../options.interface' import { IstanbulWidgetPlugin } from './IstanbulWidgetPlugin' -export type IstanbulWidgetReactPluginProps = { - id: string - name: string -} +export type IstanbulWidgetReactPluginProps = {} & PluginType export class IstanbulWidgetReactPlugin extends IstanbulWidgetPlugin { private _root!: ReactDOM.Root @@ -32,51 +30,32 @@ export class IstanbulWidgetReactPlugin extends IstanbulWidget public initialProps?: T, ) { super(id, name) - - this.registerEvents() } - public registerEvents() { - const properties = Object.getOwnPropertyNames(IstanbulWidgetReactPlugin.prototype) - - const onMethods = properties.filter((name) => name.startsWith('on') && isFunction(this[name])) - - onMethods.forEach((methodName) => { - this[methodName].call(this) - }) - } - - protected onReady() { - this.event.on('ready', () => { - this.isReady = true - }) - } - - protected onRender() { - this.event.on('render', (callback) => { - const domNode = document.createElement('div') - const root = ReactDOM.createRoot(domNode) + onRender() { + this.on('render', () => { + const el = document.createElement('div') + const root = ReactDOM.createRoot(el) root.render( React.createElement(this.Component, { ...((this.initialProps || {}) as T), id: this.id, name: this.name, + domID: this.domID, }), ) this._root = root - const target = $.ensureEl(`#${this.id}`) - target.appendChild(domNode) - callback?.({ - root, - domNode, - }) + const target = $.queryEl(`#${this.domID}`) + target.appendChild(el) + + // react式插入dom,不需要回调 }) } destory() { if (!this._root) { - console.warn('[istanbul-widget]: init component first') + IstanbulWidget.logger.warn('[istanbul-widget]: init component first') } else { this._root.unmount() } diff --git a/packages/istanbul-widget/src/utils/query.ts b/packages/istanbul-widget/src/utils/query.ts index 570928a..af216e5 100644 --- a/packages/istanbul-widget/src/utils/query.ts +++ b/packages/istanbul-widget/src/utils/query.ts @@ -9,7 +9,7 @@ const $ = { } }, - ensureEl(selector: string | HTMLElement, defaultEl?: HTMLElement) { + queryEl(selector: string | HTMLElement, defaultEl?: HTMLElement) { let target!: HTMLElement if (typeof selector === 'string') { target = document.querySelector(selector)! || defaultEl diff --git a/packages/vite-plugin-istanbul-widget/CHANGELOG.md b/packages/vite-plugin-istanbul-widget/CHANGELOG.md index 59095c0..47ef79a 100644 --- a/packages/vite-plugin-istanbul-widget/CHANGELOG.md +++ b/packages/vite-plugin-istanbul-widget/CHANGELOG.md @@ -1,5 +1,16 @@ # vite-plugin-istanbul-widget +## 1.4.0 + +### Minor Changes + +- sync istanbul-toolkit + +### Patch Changes + +- Updated dependencies + - istanbul-widget@1.4.0 + ## 1.3.3 ### Patch Changes diff --git a/packages/vite-plugin-istanbul-widget/package.json b/packages/vite-plugin-istanbul-widget/package.json index 747e702..8cfc6b8 100644 --- a/packages/vite-plugin-istanbul-widget/package.json +++ b/packages/vite-plugin-istanbul-widget/package.json @@ -1,6 +1,6 @@ { "name": "vite-plugin-istanbul-widget", - "version": "1.3.3", + "version": "1.4.0", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75d91a5..d0f729c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -168,18 +168,21 @@ importers: clsx: specifier: ^2.1.0 version: 2.1.0 + consola: + specifier: ^3.2.3 + version: 3.2.3 context-state: specifier: ^2.3.1 version: 2.3.1(react@18.2.0) + eventemitter3: + specifier: ^5.0.1 + version: 5.0.1 react: specifier: ^18.2.0 version: 18.2.0 react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) - strict-event-emitter: - specifier: ^0.5.1 - version: 0.5.1 tailwind-merge: specifier: ^2.2.2 version: 2.2.2 @@ -5170,6 +5173,11 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + /consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + dev: false + /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -6684,6 +6692,10 @@ packages: resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} dev: false + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: false + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -10661,10 +10673,6 @@ packages: bare-events: 2.2.2 dev: false - /strict-event-emitter@0.5.1: - resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} - dev: false - /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'}