diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1aa5be8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,430 @@
+
+# example-driver
+
+
+### 描述
+
+用于在线展示和编辑React组件
+
+
+### 安装
+
+```shell
+npm i --save @kne/example-driver
+```
+
+
+### 概述
+
+@kne/example-driver 是一个用于在线展示和编辑 React 组件的工具库,特别适合用于组件库文档、教程演示和技术文档中。它提供了实时代码预览和编辑功能,让用户可以直接在浏览器中查看和修改组件代码,无需搭建完整的开发环境。
+
+核心特性包括实时代码编辑、即时代码预览、语法高亮显示、错误边界处理和灵活的布局控制。支持两种展示模式:LiveCode 模式提供完整的在线编辑和实时预览能力,MiniCode 模式则通过二维码引导用户在移动端查看示例。内置 Monaco Editor 提供专业的代码编辑体验,使用 Prism 实现代码语法高亮,并通过 Debounce 优化性能,避免频繁重新渲染。
+
+适用于组件库文档网站、在线教程和培训、技术博客和文档、以及 React 组件展示平台。通过动态加载和实时编译,让文档中的示例代码真正"活"起来,用户可以直接修改代码并立即看到效果,大大提升了学习效率和用户体验。
+
+技术亮点方面,项目采用 Babel Standalone 实现浏览器端的代码编译,支持 ES2015 和 React 预设,无需后端转换。错误边界机制确保代码错误不会影响整个页面,提供友好的错误提示。支持自定义上下文组件,方便在不同场景中嵌入示例代码。Monaco Editor 配置暴露给外部,允许深度定制编辑器行为。响应式布局设计,支持单列和双列模式,适应不同屏幕尺寸。
+
+
+### 示例(全屏)
+
+#### 示例代码
+
+- 基础使用
+- 展示 ExampleDriver 组件的基本用法,包含简单的计数器示例
+- _ExampleDriver(@kne/current-lib_example-driver)[import * as _ExampleDriver from "@kne/example-driver"],(@kne/current-lib_example-driver/dist/index.css),antd(antd),remoteLoader(@kne/remote-loader)
+
+```jsx
+const {default: ExampleDriver} = _ExampleDriver;
+
+// 示例代码字符串
+const code = `
+const { Button, Card, Space } = antd;
+const { useState } = React;
+
+const Component = () => {
+ const [count, setCount] = useState(0);
+ return (
+
+
+ 计数器示例
+
+
+ {count}
+
+
+
+
+
+ );
+};
+
+render();
+`;
+
+render();
+
+```
+
+- LiveCode 模式
+- 实时代码编辑和预览模式,可以直接在浏览器中修改代码并查看效果
+- _ExampleDriver(@kne/current-lib_example-driver)[import * as _ExampleDriver from "@kne/example-driver"],(@kne/current-lib_example-driver/dist/index.css),antd(antd),remoteLoader(@kne/remote-loader)
+
+```jsx
+const {default: ExampleDriver} = _ExampleDriver;
+
+const code = `
+const { Button, Input, List, Space, Tag } = antd;
+const { useState } = React;
+
+const Component = () => {
+ const [text, setText] = useState('');
+ const [items, setItems] = useState(['学习 React', '编写组件']);
+
+ const addItem = () => {
+ if (text.trim()) {
+ setItems([...items, text.trim()]);
+ setText('');
+ }
+ };
+
+ const removeItem = (index) => {
+ setItems(items.filter((_, i) => i !== index));
+ };
+
+ return (
+
+
+
+ setText(e.target.value)}
+ placeholder="输入事项..."
+ onPressEnter={addItem}
+ />
+
+
+ (
+ removeItem(index)}>
+ 删除
+
+ ]}
+ >
+ {item}
+
+ )}
+ />
+
+
+ );
+};
+
+render();
+`;
+
+render();
+
+```
+
+- MiniCode 模式
+- 二维码预览模式,适用于移动端场景,通过二维码引导用户查看示例
+- _ExampleDriver(@kne/current-lib_example-driver)[import * as _ExampleDriver from "@kne/example-driver"],(@kne/current-lib_example-driver/dist/index.css),antd(antd),remoteLoader(@kne/remote-loader)
+
+```jsx
+const {default: ExampleDriver} = _ExampleDriver;
+
+const code = `
+const { Form, Input, Button, Space } = antd;
+const { useState } = React;
+
+const Component = () => {
+ const [form] = Form.useForm();
+ const [data, setData] = useState([]);
+
+ const onFinish = (values) => {
+ setData([...data, values]);
+ form.resetFields();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {data.length > 0 && (
+
+
已提交数据:
+ {data.map((item, index) => (
+
{item.name} - {item.email}
+ ))}
+
+ )}
+
+ );
+};
+
+render();
+`;
+
+render();
+
+```
+
+- 自定义上下文
+- 使用 contextComponent 属性自定义示例的上下文组件
+- _ExampleDriver(@kne/current-lib_example-driver)[import * as _ExampleDriver from "@kne/example-driver"],(@kne/current-lib_example-driver/dist/index.css),antd(antd),remoteLoader(@kne/remote-loader)
+
+```jsx
+const {default: ExampleDriver} = _ExampleDriver;
+
+const code = `
+const { Button, Card, Space, Switch, Tag } = antd;
+const { useState } = React;
+
+// 自定义上下文组件
+const CustomContext = ({ children }) => {
+ const [theme, setTheme] = useState('light');
+ const colors = {
+ light: { bg: '#fff', color: '#000', border: '#d9d9d9' },
+ dark: { bg: '#1a1a1a', color: '#fff', border: '#434343' }
+ };
+ const style = {
+ padding: '12px',
+ background: colors[theme].bg,
+ color: colors[theme].color,
+ borderRadius: '4px'
+ };
+
+ return (
+
+
+ setTheme(checked ? 'dark' : 'light')}
+ checkedChildren="🌙"
+ unCheckedChildren="☀️"
+ />
+
+ {theme === 'dark' ? '暗色主题' : '亮色主题'}
+
+
+ {children}
+
+ );
+};
+
+const Component = () => {
+ return (
+
+ 这是上下文包裹的内容,会根据主题自动改变颜色。
+
+ );
+};
+
+render();
+`;
+
+render( (
+ {children}
+
),
+ scope: [{
+ name: 'antd', packageName: 'antd', component: antd
+ }]
+}]}/>);
+
+```
+
+- 双列布局
+- 使用 isFull 属性控制布局,默认双列展示多个示例
+- _ExampleDriver(@kne/current-lib_example-driver)[import * as _ExampleDriver from "@kne/example-driver"],(@kne/current-lib_example-driver/dist/index.css),antd(antd),remoteLoader(@kne/remote-loader)
+
+```jsx
+const {default: ExampleDriver} = _ExampleDriver;
+
+const example1 = `
+const { Button, Card } = antd;
+
+const Component = () => {
+ return (
+
+
+
+
+ );
+};
+
+render();
+`;
+
+const example2 = `
+const { Input, Card } = antd;
+
+const Component = () => {
+ return (
+
+
+
+ );
+};
+
+render();
+`;
+
+const example3 = `
+const { Select, Card } = antd;
+const { Option } = Select;
+
+const Component = () => {
+ return (
+
+
+
+ );
+};
+
+render();
+`;
+
+const example4 = `
+const { Switch, Card } = antd;
+
+const Component = () => {
+ return (
+
+
+
+ );
+};
+
+render();
+`;
+
+// 默认双列布局
+render();
+
+```
+
+
+### API
+
+### ExampleDriver
+用于展示和编辑 React 组件示例的主组件,支持 LiveCode 和 MiniCode 两种展示模式。
+
+#### 属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| list | array | 是 | - | 示例列表数组,每个元素代表一个示例 |
+| isFull | boolean | 否 | false | 是否全宽显示,true 时单列显示,false 时双列显示 |
+| contextComponent | React Component | 否 | - | 自定义上下文组件,用于包裹渲染的代码内容 |
+| className | string | 否 | - | 自定义 CSS 类名 |
+| ...props | object | 否 | - | 其他 HTML div 元素的属性 |
+
+#### list 数组项属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| code | string | 是 | - | 示例代码字符串 |
+| scope | array | 是 | - | 作用域数组,包含组件和包信息 |
+| title | string | 是 | - | 示例标题 |
+| description | string | 是 | - | 示例描述,支持 HTML 格式 |
+| qrcodeUrl | string | 否 | - | 二维码图片 URL,传入此项则使用 MiniCode 模式 |
+| contextComponent | React Component | 否 | - | 单个示例的自定义上下文组件 |
+
+#### scope 数组项属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| component | React Component | 是 | - | 要暴露给示例代码的组件 |
+| name | string | 是 | - | 组件名称,在代码中使用 |
+| packageName | string | 否 | - | 包名,用于生成导入语句 |
+| importStatement | string | 否 | - | 自定义导入语句,覆盖自动生成的导入 |
+
+### LiveCode
+提供实时代码编辑和预览功能的子组件(通过 ExampleDriver 自动使用)。
+
+#### 属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| code | string | 是 | - | 要展示的代码 |
+| scope | array | 是 | - | 作用域数组 |
+| title | string | 是 | - | 标题 |
+| description | string | 是 | - | 描述 |
+| contextComponent | React Component | 否 | - | 上下文组件 |
+
+### MiniCode
+显示二维码预览的只读模式子组件(通过 ExampleDriver 自动使用)。
+
+#### 属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| code | string | 是 | - | 示例代码 |
+| qrcodeUrl | string | 是 | - | 二维码图片地址 |
+| scope | array | 是 | - | 作用域数组 |
+| title | string | 是 | - | 标题 |
+| description | string | 是 | - | 描述 |
+
+### config
+Monaco Editor 的配置对象,用于自定义编辑器行为。
+
+#### 方法说明
+| 方法名 | 参数 | 返回值 | 说明 |
+|--------|------|--------|------|
+| config | options: object | void | 配置 Monaco Editor 的加载选项,如模块路径等 |
+
diff --git a/doc/api.md b/doc/api.md
new file mode 100644
index 0000000..b05f32b
--- /dev/null
+++ b/doc/api.md
@@ -0,0 +1,61 @@
+### ExampleDriver
+用于展示和编辑 React 组件示例的主组件,支持 LiveCode 和 MiniCode 两种展示模式。
+
+#### 属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| list | array | 是 | - | 示例列表数组,每个元素代表一个示例 |
+| isFull | boolean | 否 | false | 是否全宽显示,true 时单列显示,false 时双列显示 |
+| contextComponent | React Component | 否 | - | 自定义上下文组件,用于包裹渲染的代码内容 |
+| className | string | 否 | - | 自定义 CSS 类名 |
+| ...props | object | 否 | - | 其他 HTML div 元素的属性 |
+
+#### list 数组项属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| code | string | 是 | - | 示例代码字符串 |
+| scope | array | 是 | - | 作用域数组,包含组件和包信息 |
+| title | string | 是 | - | 示例标题 |
+| description | string | 是 | - | 示例描述,支持 HTML 格式 |
+| qrcodeUrl | string | 否 | - | 二维码图片 URL,传入此项则使用 MiniCode 模式 |
+| contextComponent | React Component | 否 | - | 单个示例的自定义上下文组件 |
+
+#### scope 数组项属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| component | React Component | 是 | - | 要暴露给示例代码的组件 |
+| name | string | 是 | - | 组件名称,在代码中使用 |
+| packageName | string | 否 | - | 包名,用于生成导入语句 |
+| importStatement | string | 否 | - | 自定义导入语句,覆盖自动生成的导入 |
+
+### LiveCode
+提供实时代码编辑和预览功能的子组件(通过 ExampleDriver 自动使用)。
+
+#### 属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| code | string | 是 | - | 要展示的代码 |
+| scope | array | 是 | - | 作用域数组 |
+| title | string | 是 | - | 标题 |
+| description | string | 是 | - | 描述 |
+| contextComponent | React Component | 否 | - | 上下文组件 |
+
+### MiniCode
+显示二维码预览的只读模式子组件(通过 ExampleDriver 自动使用)。
+
+#### 属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| code | string | 是 | - | 示例代码 |
+| qrcodeUrl | string | 是 | - | 二维码图片地址 |
+| scope | array | 是 | - | 作用域数组 |
+| title | string | 是 | - | 标题 |
+| description | string | 是 | - | 描述 |
+
+### config
+Monaco Editor 的配置对象,用于自定义编辑器行为。
+
+#### 方法说明
+| 方法名 | 参数 | 返回值 | 说明 |
+|--------|------|--------|------|
+| config | options: object | void | 配置 Monaco Editor 的加载选项,如模块路径等 |
diff --git a/doc/base.js b/doc/base.js
new file mode 100644
index 0000000..8097db3
--- /dev/null
+++ b/doc/base.js
@@ -0,0 +1,32 @@
+const {default: ExampleDriver} = _ExampleDriver;
+
+// 示例代码字符串
+const code = `
+const { Button, Card, Space } = antd;
+const { useState } = React;
+
+const Component = () => {
+ const [count, setCount] = useState(0);
+ return (
+
+
+ 计数器示例
+
+
+ {count}
+
+
+
+
+
+ );
+};
+
+render();
+`;
+
+render();
diff --git a/doc/context.js b/doc/context.js
new file mode 100644
index 0000000..e322760
--- /dev/null
+++ b/doc/context.js
@@ -0,0 +1,60 @@
+const {default: ExampleDriver} = _ExampleDriver;
+
+const code = `
+const { Button, Card, Space, Switch, Tag } = antd;
+const { useState } = React;
+
+// 自定义上下文组件
+const CustomContext = ({ children }) => {
+ const [theme, setTheme] = useState('light');
+ const colors = {
+ light: { bg: '#fff', color: '#000', border: '#d9d9d9' },
+ dark: { bg: '#1a1a1a', color: '#fff', border: '#434343' }
+ };
+ const style = {
+ padding: '12px',
+ background: colors[theme].bg,
+ color: colors[theme].color,
+ borderRadius: '4px'
+ };
+
+ return (
+
+
+ setTheme(checked ? 'dark' : 'light')}
+ checkedChildren="🌙"
+ unCheckedChildren="☀️"
+ />
+
+ {theme === 'dark' ? '暗色主题' : '亮色主题'}
+
+
+ {children}
+
+ );
+};
+
+const Component = () => {
+ return (
+
+ 这是上下文包裹的内容,会根据主题自动改变颜色。
+
+ );
+};
+
+render();
+`;
+
+render( (
+ {children}
+
),
+ scope: [{
+ name: 'antd', packageName: 'antd', component: antd
+ }]
+}]}/>);
diff --git a/doc/example.json b/doc/example.json
new file mode 100644
index 0000000..326c25f
--- /dev/null
+++ b/doc/example.json
@@ -0,0 +1,120 @@
+{
+ "isFull": true,
+ "list": [
+ {
+ "title": "基础使用",
+ "description": "展示 ExampleDriver 组件的基本用法,包含简单的计数器示例",
+ "code": "./base.js",
+ "scope": [
+ {
+ "name": "_ExampleDriver",
+ "packageName": "@kne/current-lib_example-driver",
+ "importStatement": "import * as _ExampleDriver from \"@kne/example-driver\""
+ },
+ {
+ "packageName": "@kne/current-lib_example-driver/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ },
+ {
+ "title": "LiveCode 模式",
+ "description": "实时代码编辑和预览模式,可以直接在浏览器中修改代码并查看效果",
+ "code": "./livecode.js",
+ "scope": [
+ {
+ "name": "_ExampleDriver",
+ "packageName": "@kne/current-lib_example-driver",
+ "importStatement": "import * as _ExampleDriver from \"@kne/example-driver\""
+ },
+ {
+ "packageName": "@kne/current-lib_example-driver/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ },
+ {
+ "title": "MiniCode 模式",
+ "description": "二维码预览模式,适用于移动端场景,通过二维码引导用户查看示例",
+ "code": "./minicode.js",
+ "scope": [
+ {
+ "name": "_ExampleDriver",
+ "packageName": "@kne/current-lib_example-driver",
+ "importStatement": "import * as _ExampleDriver from \"@kne/example-driver\""
+ },
+ {
+ "packageName": "@kne/current-lib_example-driver/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ },
+ {
+ "title": "自定义上下文",
+ "description": "使用 contextComponent 属性自定义示例的上下文组件",
+ "code": "./context.js",
+ "scope": [
+ {
+ "name": "_ExampleDriver",
+ "packageName": "@kne/current-lib_example-driver",
+ "importStatement": "import * as _ExampleDriver from \"@kne/example-driver\""
+ },
+ {
+ "packageName": "@kne/current-lib_example-driver/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ },
+ {
+ "title": "双列布局",
+ "description": "使用 isFull 属性控制布局,默认双列展示多个示例",
+ "code": "./layout.js",
+ "scope": [
+ {
+ "name": "_ExampleDriver",
+ "packageName": "@kne/current-lib_example-driver",
+ "importStatement": "import * as _ExampleDriver from \"@kne/example-driver\""
+ },
+ {
+ "packageName": "@kne/current-lib_example-driver/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ }
+ ]
+}
diff --git a/doc/layout.js b/doc/layout.js
new file mode 100644
index 0000000..5fbc7fd
--- /dev/null
+++ b/doc/layout.js
@@ -0,0 +1,85 @@
+const {default: ExampleDriver} = _ExampleDriver;
+
+const example1 = `
+const { Button, Card } = antd;
+
+const Component = () => {
+ return (
+
+
+
+
+ );
+};
+
+render();
+`;
+
+const example2 = `
+const { Input, Card } = antd;
+
+const Component = () => {
+ return (
+
+
+
+ );
+};
+
+render();
+`;
+
+const example3 = `
+const { Select, Card } = antd;
+const { Option } = Select;
+
+const Component = () => {
+ return (
+
+
+
+ );
+};
+
+render();
+`;
+
+const example4 = `
+const { Switch, Card } = antd;
+
+const Component = () => {
+ return (
+
+
+
+ );
+};
+
+render();
+`;
+
+// 默认双列布局
+render();
diff --git a/doc/livecode.js b/doc/livecode.js
new file mode 100644
index 0000000..33eed0a
--- /dev/null
+++ b/doc/livecode.js
@@ -0,0 +1,61 @@
+const {default: ExampleDriver} = _ExampleDriver;
+
+const code = `
+const { Button, Input, List, Space, Tag } = antd;
+const { useState } = React;
+
+const Component = () => {
+ const [text, setText] = useState('');
+ const [items, setItems] = useState(['学习 React', '编写组件']);
+
+ const addItem = () => {
+ if (text.trim()) {
+ setItems([...items, text.trim()]);
+ setText('');
+ }
+ };
+
+ const removeItem = (index) => {
+ setItems(items.filter((_, i) => i !== index));
+ };
+
+ return (
+
+
+
+ setText(e.target.value)}
+ placeholder="输入事项..."
+ onPressEnter={addItem}
+ />
+
+
+ (
+ removeItem(index)}>
+ 删除
+
+ ]}
+ >
+ {item}
+
+ )}
+ />
+
+
+ );
+};
+
+render();
+`;
+
+render();
diff --git a/doc/minicode.js b/doc/minicode.js
new file mode 100644
index 0000000..e040b78
--- /dev/null
+++ b/doc/minicode.js
@@ -0,0 +1,56 @@
+const {default: ExampleDriver} = _ExampleDriver;
+
+const code = `
+const { Form, Input, Button, Space } = antd;
+const { useState } = React;
+
+const Component = () => {
+ const [form] = Form.useForm();
+ const [data, setData] = useState([]);
+
+ const onFinish = (values) => {
+ setData([...data, values]);
+ form.resetFields();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {data.length > 0 && (
+
+
已提交数据:
+ {data.map((item, index) => (
+
{item.name} - {item.email}
+ ))}
+
+ )}
+
+ );
+};
+
+render();
+`;
+
+render();
diff --git a/doc/summary.md b/doc/summary.md
new file mode 100644
index 0000000..3e48adf
--- /dev/null
+++ b/doc/summary.md
@@ -0,0 +1,7 @@
+@kne/example-driver 是一个用于在线展示和编辑 React 组件的工具库,特别适合用于组件库文档、教程演示和技术文档中。它提供了实时代码预览和编辑功能,让用户可以直接在浏览器中查看和修改组件代码,无需搭建完整的开发环境。
+
+核心特性包括实时代码编辑、即时代码预览、语法高亮显示、错误边界处理和灵活的布局控制。支持两种展示模式:LiveCode 模式提供完整的在线编辑和实时预览能力,MiniCode 模式则通过二维码引导用户在移动端查看示例。内置 Monaco Editor 提供专业的代码编辑体验,使用 Prism 实现代码语法高亮,并通过 Debounce 优化性能,避免频繁重新渲染。
+
+适用于组件库文档网站、在线教程和培训、技术博客和文档、以及 React 组件展示平台。通过动态加载和实时编译,让文档中的示例代码真正"活"起来,用户可以直接修改代码并立即看到效果,大大提升了学习效率和用户体验。
+
+技术亮点方面,项目采用 Babel Standalone 实现浏览器端的代码编译,支持 ES2015 和 React 预设,无需后端转换。错误边界机制确保代码错误不会影响整个页面,提供友好的错误提示。支持自定义上下文组件,方便在不同场景中嵌入示例代码。Monaco Editor 配置暴露给外部,允许深度定制编辑器行为。响应式布局设计,支持单列和双列模式,适应不同屏幕尺寸。
diff --git a/example/craco.config.js b/example/craco.config.js
new file mode 100644
index 0000000..ea84dba
--- /dev/null
+++ b/example/craco.config.js
@@ -0,0 +1,32 @@
+const {CracoLibsExamplePlugin, env} = require('@kne/modules-dev');
+const aliasConfig = require('./webstorm.webpack.config');
+const packageJson = require('../package.json');
+
+process.env.CI = false;
+
+module.exports = {
+ webpack: {
+ alias: aliasConfig.resolve.alias, configure: (webpackConfig) => {
+ const definePlugin = webpackConfig.plugins.find((plugin) => plugin.constructor.name === 'DefinePlugin');
+ Object.assign(definePlugin.definitions['process.env'], {
+ DEFAULT_VERSION: `"${packageJson.version}"`
+ });
+ return webpackConfig;
+ }
+ }, plugins: [{
+ plugin: CracoLibsExamplePlugin, options: {
+ middleware: (moduleFederationConfig) => {
+ const shared = Object.assign({}, moduleFederationConfig.shared,{
+ '@kne/current-lib_example-driver': {
+ singleton: true, requiredVersion: false
+ }
+ });
+ return Object.assign({}, moduleFederationConfig, {
+ exposes: {
+ './components': env.manifestPath
+ }, shared
+ })
+ }
+ }
+ }]
+};
diff --git a/example/package.json b/example/package.json
index 4241749..a406229 100644
--- a/example/package.json
+++ b/example/package.json
@@ -1,30 +1,55 @@
{
- "name": "example-driver",
- "homepage": "/example-driver",
- "version": "0.0.0",
- "private": true,
- "scripts": {
- "start": "node ../node_modules/react-scripts/bin/react-scripts.js start",
- "build": "node ../node_modules/react-scripts/bin/react-scripts.js build",
- "test": "node ../node_modules/react-scripts/bin/react-scripts.js test",
- "eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject"
- },
+ "name": "@kne-components/example-driver",
+ "version": "0.1.0",
"dependencies": {
- "@kne/example-driver": "file:..",
+ "@kne/current-lib_example-driver": "file:..",
"react": "file:../node_modules/react",
- "react-dom": "file:../node_modules/react-dom",
- "react-scripts": "file:../node_modules/react-scripts"
+ "react-dom": "file:../node_modules/react-dom"
+ },
+ "files": [
+ "build"
+ ],
+ "scripts": {
+ "init": "npm install --legacy-peer-deps",
+ "start": "cross-env PORT=3020 MODULES_DEV_PUBLIC_URL=/ craco start",
+ "build": "cross-env COMPONENTS_NAME=example-driver MODULES_DEV_PUBLIC_URL=/example-driver craco build"
},
"eslintConfig": {
- "extends": "react-app"
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
},
- "browserslist": [
- ">0.2%",
- "not dead",
- "not ie <= 11",
- "not op_mini all"
- ],
"devDependencies": {
- "@kne/react-fetch": "^1.1.13"
+ "@craco/craco": "^7.1.0",
+ "@kne/axios-fetch": "^1.0.7",
+ "@kne/craco-module-federation": "^1.1.2",
+ "@kne/modules-dev": "^2.0.6",
+ "@kne/react-fetch": "^1.4.2",
+ "@kne/remote-loader": "^1.2.3",
+ "antd": "^5.13.3",
+ "axios": "^1.6.7",
+ "classnames": "^2.5.1",
+ "cross-env": "^7.0.3",
+ "http-proxy-middleware": "^2.0.6",
+ "husky": "^8.0.3",
+ "lint-staged": "^13.3.0",
+ "lodash": "^4.17.21",
+ "sass": "^1.75.0",
+ "prettier": "^2.8.8",
+ "react-router-dom": "^6.22.0",
+ "react-scripts": "^5.0.1"
}
}
diff --git a/example/public/favicon.ico b/example/public/favicon.ico
index a11777c..e69de29 100644
Binary files a/example/public/favicon.ico and b/example/public/favicon.ico differ
diff --git a/example/public/favicon.svg b/example/public/favicon.svg
new file mode 100644
index 0000000..8ee9c9e
--- /dev/null
+++ b/example/public/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/example/public/index.html b/example/public/index.html
index ee3821b..ed1c3c4 100644
--- a/example/public/index.html
+++ b/example/public/index.html
@@ -1,48 +1,42 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
- react-form-antd
-
-
-
-
-
-
-
+ manifest.json provides metadata used when your web app is installed on a
+ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
+ -->
+
+ [@kne/example-driver] - KneUnion Libs
+
+
+
+
+
+
+
- To begin the development, run `npm start` or `yarn start`.
- To create a production bundle, use `npm run build` or `yarn build`.
- -->
-
diff --git a/example/public/logo192.png b/example/public/logo192.png
new file mode 100644
index 0000000..fc44b0a
Binary files /dev/null and b/example/public/logo192.png differ
diff --git a/example/public/logo512.png b/example/public/logo512.png
new file mode 100644
index 0000000..a4e47a6
Binary files /dev/null and b/example/public/logo512.png differ
diff --git a/example/public/manifest.json b/example/public/manifest.json
index da47227..080d6c7 100644
--- a/example/public/manifest.json
+++ b/example/public/manifest.json
@@ -1,11 +1,21 @@
{
- "short_name": "react-form",
- "name": "react-form",
+ "short_name": "React App",
+ "name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
}
],
"start_url": ".",
diff --git a/example/public/mock/data.json b/example/public/mock/data.json
index d1e3193..e69de29 100644
--- a/example/public/mock/data.json
+++ b/example/public/mock/data.json
@@ -1,19 +0,0 @@
-{
- "code": 0,
- "data": {
- "list": [
- {
- "id": 1,
- "name": "1撒打算大的"
- },
- {
- "id": 2,
- "name": "2大方大方大方"
- },
- {
- "id": 3,
- "name": "3西西说大方水电费速度"
- }
- ]
- }
-}
diff --git a/example/public/mock/error.json b/example/public/mock/error.json
index 5dcbbbc..e69de29 100644
--- a/example/public/mock/error.json
+++ b/example/public/mock/error.json
@@ -1,20 +0,0 @@
-{
- "code": 502,
- "msg": "错误信息",
- "data": {
- "list": [
- {
- "id": 1,
- "name": "1撒打算大的"
- },
- {
- "id": 2,
- "name": "2大方大方大方"
- },
- {
- "id": 3,
- "name": "3西西说大方水电费速度"
- }
- ]
- }
-}
diff --git a/example/public/qrcode.jpg b/example/public/qrcode.jpg
index 5e82546..e69de29 100644
Binary files a/example/public/qrcode.jpg and b/example/public/qrcode.jpg differ
diff --git a/example/src/App.js b/example/src/App.js
index 0e4b367..aa9b906 100644
--- a/example/src/App.js
+++ b/example/src/App.js
@@ -1,34 +1,29 @@
-import ExampleDriver from '@kne/example-driver';
-import * as ReactFetch from '@kne/react-fetch';
+import { HashRouter } from "react-router-dom";
+import createEntry from "@kne/modules-dev/dist/create-entry.modern";
+import "@kne/modules-dev/dist/create-entry.css";
+import readme from "readme";
-const App = () => {
- return {\n console.log(config);\n return new Promise((resolve) => {\n setTimeout(() => {\n resolve({\n data: {\n code: 0,\n data: [\n {title: '数据一'},\n {title: '数据二'}\n ]\n }\n });\n }, 1000);\n });\n },\n loading: 'loading....',\n empty: '空',\n transformResponse: (response) => {\n const {data} = response;\n response.data = {\n code: data.code === 0 ? 200 : data.code, msg: data.msg, results: data.data\n };\n return response;\n }\n});\n\nconst Remote = createWithFetch({\n url: '/react-fetch/mock/data.json',\n params: {page: 1},\n updateType: 'nextPage'\n})(({data, send, reload, refresh, loadMore, requestParams}) => {\n return data.map((item, index) => {\n return {item.title}
\n });\n});\n\nrender();\n\n"
- }, {
- "title": "基本用法",
- "description": "注意:react-fetch 内部处理请求的时候只通过 code,msg,results来作为内部逻辑,code为200判定为请求成功,不为200时判定为错误,msg会传入到error组件,拿到results后,会将results作为业务组件的data属性\n如果后端的返回不满足上诉格式,需要在preset的transformResponse方法做转换适配\najax为一个axios实例,每个实例的拦截器可能不同,默认会在内部自动创建一个axios实例,但是没有任何拦截器,如果想给其添加拦截器,可以自行创建axios实例通过preset设置\npreset 可以单独放一个文件里,在入口文件顶部引入",
- "isFull": false,
- "scope": [{
- "name": "ReactFetch", "packageName": "@kne/react-fetch", "component": ReactFetch
- }],
- "code": "const {createWithFetch, preset} = ReactFetch;\n\npreset({\n ajax: (config) => {\n console.log(config);\n return new Promise((resolve) => {\n setTimeout(() => {\n resolve({\n data: {\n code: 0,\n data: [\n {title: '数据一'},\n {title: '数据二'}\n ]\n }\n });\n }, 1000);\n });\n },\n loading: 'loading....',\n empty: '空',\n transformResponse: (response) => {\n const {data} = response;\n response.data = {\n code: data.code === 0 ? 200 : data.code, msg: data.msg, results: data.data\n };\n return response;\n }\n});\n\nconst Remote = createWithFetch({\n url: '/react-fetch/mock/data.json',\n params: {page: 1},\n updateType: 'nextPage'\n})(({data, send, reload, refresh, loadMore, requestParams}) => {\n return data.map((item, index) => {\n return {item.title}
\n });\n});\n\nrender();\n\n"
- }, {
- "title": "状态标签",
- "description": "这里填写示例说明",
- "qrcodeUrl": "/example-driver/qrcode.jpg",
- "scope": [{
- "name": "miniCore", "packageName": "@kne/mini-core"
- }, {
- "name": "lodash", "packageName": "lodash"
- }],
- "code": "const {StateTag} = miniCore;\nconst BaseExample = () => {\n return 哈哈哈;\n};\n\nrender();\n\n"
- }]}/>
+const ExampleRoutes = createEntry.ExampleRoutes;
+
+const App = ({ globalPreset, ...props }) => {
+ return (
+
+
+
+ );
};
export default App;
diff --git a/example/src/bootstrap.js b/example/src/bootstrap.js
new file mode 100644
index 0000000..e5c19bb
--- /dev/null
+++ b/example/src/bootstrap.js
@@ -0,0 +1,13 @@
+import { globalInit } from './preset';
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import App from './App';
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+
+const renderRoot = async (App) => {
+ const globalPreset = await globalInit();
+ return root.render();
+};
+
+renderRoot(App);
diff --git a/example/src/index.js b/example/src/index.js
index 1d247ae..b93c7a0 100644
--- a/example/src/index.js
+++ b/example/src/index.js
@@ -1,7 +1 @@
-import '@kne/example-driver/dist/index.css';
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-import App from './App';
-
-const root = ReactDOM.createRoot(document.getElementById('root'));
-root.render();
+import('./bootstrap');
diff --git a/example/src/preset.js b/example/src/preset.js
new file mode 100644
index 0000000..6f0ca1a
--- /dev/null
+++ b/example/src/preset.js
@@ -0,0 +1,61 @@
+import React from 'react';
+import { preset as fetchPreset } from '@kne/react-fetch';
+import { Spin, Empty, message } from 'antd';
+import createAjax from '@kne/axios-fetch';
+import { preset as remoteLoaderPreset } from '@kne/remote-loader';
+
+window.PUBLIC_URL = process.env.PUBLIC_URL;
+
+// url: 'https://registry.npmmirror.com',
+// tpl: '{{url}}/@kne-components%2f{{remote}}/{{version}}/files/build',
+
+// url: 'https://cdn.jsdelivr.net', tpl: '{{url}}/npm/@kne-components/{{remote}}@{{version}}/build'
+
+const registry = {
+ url: 'https://uc.fatalent.cn', tpl: '{{url}}/packages/@kne-components/{{remote}}/{{version}}/build'
+};
+
+export const globalInit = async () => {
+ const ajax = createAjax({
+ errorHandler: error => message.error(error)
+ });
+
+ const componentsCoreRemote = {
+ ...registry, remote: 'components-core', defaultVersion: '0.4.51'
+ };
+
+ remoteLoaderPreset({
+ remotes: {
+ default: componentsCoreRemote, 'components-core': componentsCoreRemote, 'components-iconfont': {
+ ...registry, remote: 'components-iconfont', defaultVersion: '0.2.1'
+ }, 'example-driver': process.env.NODE_ENV === 'development' ? {
+ remote: 'example-driver', url: '/', tpl: '{{url}}'
+ } : {
+ ...registry, remote: 'example-driver', defaultVersion: process.env.DEFAULT_VERSION
+ }
+ }
+ });
+
+
+ fetchPreset({
+ ajax, loading: , error: null, empty: , transformResponse: (response) => {
+ const { data } = response;
+ response.data = {
+ code: data.code === 0 ? 200 : data.code, msg: data.msg, results: data.data
+ };
+ return response;
+ }
+ });
+
+ return {
+ ajax, enums: {}, apis: {}, themeToken: {
+ colorPrimary: '#4183F0'
+ }
+ };
+};
diff --git a/example/src/setupTests.js b/example/src/setupTests.js
new file mode 100644
index 0000000..b28b910
--- /dev/null
+++ b/example/src/setupTests.js
@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';
\ No newline at end of file
diff --git a/example/webstorm.webpack.config.js b/example/webstorm.webpack.config.js
new file mode 100644
index 0000000..49dbd2e
--- /dev/null
+++ b/example/webstorm.webpack.config.js
@@ -0,0 +1,12 @@
+'use strict'
+const path = require('path')
+
+module.exports = {
+ context: path.resolve(__dirname),
+ resolve: {
+ extensions: ['.js', '.jsx', '.json'],
+ alias: {
+ "@root": path.resolve("./src")
+ }
+ }
+}
diff --git a/package.json b/package.json
index 4d9cc48..ffd528c 100644
--- a/package.json
+++ b/package.json
@@ -1,26 +1,34 @@
{
"name": "@kne/example-driver",
- "version": "0.1.12",
+ "version": "0.1.13",
"description": "用于在线展示和编辑React组件",
+ "syntax": {
+ "esmodules": true
+ },
+ "types": "dist/index.d.ts",
"main": "dist/index.js",
"module": "dist/index.modern.js",
"source": "src/index.js",
"scripts": {
- "init": "npm install && cd example && npm install",
- "start": "run-p start:lib start:example",
- "build": "run-s build:lib build:example",
- "build:lib": "microbundle --no-compress --format modern,cjs --jsx React.createElement --jsxFragment React.Fragment",
- "build:md": "create-md",
+ "init": "husky && npm run init-example",
+ "start": "run-p start:lib start:md start:example",
+ "build": "run-s build:lib build:md build:example",
+ "init-example": "modules-dev-libs-init",
+ "build:md": "npx @kne/md-doc",
+ "start:md": "npx @kne/md-doc --watch",
+ "build:types": "cp src/index.d.ts dist/",
+ "build:package-manifest": "cp src/package-manifest.json dist/",
+ "build:lib-main": "microbundle --no-compress --format modern,cjs --jsx React.createElement --jsxFragment React.Fragment",
+ "build:lib": "run-s build:lib-main build:types build:package-manifest",
"start:lib": "microbundle watch --no-compress --format modern,cjs --jsx React.createElement --jsxFragment React.Fragment",
- "test": "run-s test:unit test:lint test:build",
+ "build:example": "cd example && npm run build",
+ "start:example": "cd example && npm run start",
"test:build": "run-s build",
"test:lint": "eslint .",
"test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
"test:watch": "react-scripts test --env=jsdom",
- "build:example": "cd example && npm run build",
- "start:example": "cd example && npm run start",
- "deploy": "gh-pages -d example/build",
- "prettier": "prettier --config .prettierrc --write 'src/**/*.{js,jsx,ts,tsx,json,css,scss,md}'"
+ "prettier": "prettier --config .prettierrc --write '{src/**/*,index,prompts}.{js,jsx,ts,tsx,json,css,scss}'",
+ "lint-staged": "npx lint-staged"
},
"repository": {
"type": "git",
@@ -47,6 +55,7 @@
},
"homepage": "https://github.com/kne-union/react-fetch#readme",
"peerDependencies": {
+ "@monaco-editor/loader": "*",
"axios": ">=0.19.2",
"prop-types": ">=15.x",
"react": ">=16.x",
@@ -54,18 +63,23 @@
},
"devDependencies": {
"@kne/md-doc": "^0.1.8",
- "axios": "^0.26.1",
+ "@kne/microbundle": "^0.15.5",
+ "@kne/modules-dev": "^2.1.18",
+ "antd": "^5.21.6",
+ "axios": "^1.12.0",
"cross-env": "^7.0.3",
- "gh-pages": "^3.2.3",
- "husky": "^7.0.4",
- "microbundle": "^0.14.2",
- "node-sass": "^9.0.0",
+ "gh-pages": "^5.0.0",
+ "husky": "^9.0.11",
"npm-run-all": "^4.1.5",
"prettier": "^2.5.1",
- "react-scripts": "^5.0.0"
+ "react-scripts": "^5.0.0",
+ "sass": "^1.97.3",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
},
"dependencies": {
"@babel/standalone": "^7.24.0",
+ "@kne/react-error-boundary": "^0.1.1",
"@monaco-editor/react": "^4.6.0",
"babel-standalone": "^6.26.0",
"classnames": "^2.3.1",
diff --git "a/prompts/\345\221\275\345\220\215\347\244\272\344\276\213\347\274\226\345\206\231\347\244\272\344\276\213\346\217\217\350\277\260.md" "b/prompts/\345\221\275\345\220\215\347\244\272\344\276\213\347\274\226\345\206\231\347\244\272\344\276\213\346\217\217\350\277\260.md"
new file mode 100644
index 0000000..c7952d3
--- /dev/null
+++ "b/prompts/\345\221\275\345\220\215\347\244\272\344\276\213\347\274\226\345\206\231\347\244\272\344\276\213\346\217\217\350\277\260.md"
@@ -0,0 +1,50 @@
+# 任务:完善示例文档的标题和描述
+
+## 目标
+根据 `doc/example.json` 中 `code` 字段引用的代码实现内容,完善该文件中的 `title` 和 `description` 字段。
+
+## 具体要求
+
+### 1. 分析代码实现
+- 仔细阅读 `doc/example.json` 中每个示例项的 `code` 字段引用的代码文件
+- 理解每个代码示例的核心功能和实现方式
+- 识别组件的主要用途和特点
+
+### 2. 编写标题 (title)
+- **简洁明了**:用4-8个字概括示例的核心功能
+- **突出重点**:体现示例的最主要特性
+- **用户视角**:从使用者的角度命名
+- **避免技术术语**:使用通俗易懂的表达
+
+### 3. 编写描述 (description)
+- **功能说明**:清晰描述示例展示的功能
+- **使用场景**:说明适用的业务场景
+- **特点优势**:突出实现的特点或优势
+- **长度控制**:建议在20-50字之间
+
+## 示例格式
+
+### 原始结构
+```json
+{
+ "code": "./form-modal.js",
+ "title": "",
+ "description": ""
+}
+```
+
+### 优化后示例
+```json
+{
+ "code": "./form-modal.js",
+ "title": "模态框表单",
+ "description": "在模态框中展示表单,支持弹窗内数据提交和验证"
+}
+```
+
+## 注意事项
+- 不要修改 `code` 字段的路径引用
+- 确保每个示例都有完整的 `title` 和 `description`
+- 描述要准确反映代码实现的功能
+- 保持所有示例的命名风格一致
+- 重点突出用户体验和实用价值
\ No newline at end of file
diff --git "a/prompts/\345\233\275\351\231\205\345\214\226.md" "b/prompts/\345\233\275\351\231\205\345\214\226.md"
new file mode 100644
index 0000000..9ec750e
--- /dev/null
+++ "b/prompts/\345\233\275\351\231\205\345\214\226.md"
@@ -0,0 +1,217 @@
+# 组件国际化指南
+
+## 概述
+
+本指南用于将组件完成国际化。
+
+## 一、创建的文件
+
+1. **`src/withLocale.js`** - 统一的国际化 HOC,所有组件共用
+2. **`src/locale/zh-CN.js`** - 中文语言包,所有组件共用
+3. **`src/locale/en-US.js`** - 英文语言包,所有组件共用
+
+## 二、需要修改的文件类型
+
+### 主组件文件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+
+### FormInner 表单组件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+- 表单 label 国际化
+
+### getColumns 等工具函数
+- 通过参数接收 `formatMessage`
+- 移除内部的 `useIntl` 和 `withLocale` 引入
+
+### Action 操作组件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+
+## 三、国际化的关键模式
+
+### 1. useIntl Hook 使用
+```javascript
+import { useIntl } from '@kne/react-intl';
+
+const Component = () => {
+ const { formatMessage } = useIntl();
+ return {formatMessage({ id: 'ComponentName.Key' })}
;
+};
+```
+
+### 2. withLocale 包裹普通组件
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = ({ ...props }) => {
+ const { formatMessage } = useIntl();
+ // 将所有中文替换为 formatMessage({ id: 'ComponentName.Key' })
+ return {formatMessage({ id: 'ComponentName.Key' })}
;
+};
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+### 3. createWithRemoteLoader 组件(推荐格式)
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // ...
+});
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+**注意:** 对于 `createWithRemoteLoader` 创建的组件,可以先用 `ComponentInner` 存储,再用 `withLocale` 包裹。
+
+### 4. getColumns 等工具函数(formatMessage 从父组件传入)
+```javascript
+const getColumns = ({formatMessage}) => {
+ return [
+ {
+ name: 'name',
+ title: formatMessage({ id: 'ComponentName.Key' })
+ }
+ ];
+};
+
+// 父组件中使用
+const columns = getColumns({formatMessage});
+```
+
+### 5. 带参数的翻译
+```javascript
+formatMessage({ id: 'ComponentName.KeyWithParam' }, { name: value })
+```
+
+## 四、注意事项
+
+1. **所有使用 `useIntl` 的组件必须用 `withLocale` 包裹**
+2. **`getColumns` 等工具函数通过参数接收 `formatMessage`,不使用 `useIntl`**
+3. **语言包中避免重复的 key**,命名规则:`组件名 + 功能名`,如 `PersonalCard.status.online`
+4. **`createWithRemoteLoader` 创建的组件内部使用 useIntl 时,外层需要重命名并用 withLocale 包裹**
+5. 注意检查 `withLocale`文件的引用地址,根目录组件使用 `../withLocale`
+
+---
+
+# 组件国际化操作步骤
+
+## 步骤
+
+### 1. 更新语言包文件
+在 `src/locale/zh-CN.js` 和 `src/locale/en-US.js` 中添加对应的翻译文本:
+```javascript
+// zh-CN.js
+const locale = {
+ ComponentName: {
+ status: {
+ online: '在线',
+ offline: '离线'
+ },
+ summary: '简介'
+ }
+};
+
+// en-US.js
+const locale = {
+ ComponentName: {
+ status: {
+ online: 'Online',
+ offline: 'Offline'
+ },
+ summary: 'Summary'
+ }
+};
+```
+
+### 2. 修改组件文件
+
+#### 主组件修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = ({ ...props }) => {
+ const { formatMessage } = useIntl();
+ // 将所有中文替换为 formatMessage({ id: 'ComponentName.Key' })
+ return (
+ // ...
+ );
+};
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+#### FormInner 修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const FormInnerInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // label={formatMessage({ id: 'ComponentName.Key' })}
+ // ...
+});
+
+const FormInner = withLocale(FormInnerInner);
+
+export default FormInner;
+```
+
+#### Action 操作组件修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ActionInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // ...
+});
+
+const ActionComponent = withLocale(ActionInner);
+
+export default ActionComponent;
+```
+
+#### getColumns 修改模式:
+```javascript
+// 移除 useIntl 和 withLocale 引入
+const getColumns = ({formatMessage}) => {
+ return [
+ {
+ name: 'xxx',
+ title: formatMessage({ id: 'ComponentName.Key' })
+ }
+ ];
+};
+```
+
+父组件中调用:`getColumns({formatMessage})`
+
+### 3. 语言包 key 命名规范
+- 避免重复,使用 `组件名+功能名` 格式,如 `PersonalCard.status.online`、`UserName`、`UserRole`、`SettingType`
+- 中文和英文语言包保持完全一致的 key 结构
+
+### 4. 检查要点
+- [ ] 所有使用 `useIntl` 的组件都用 `withLocale` 包裹
+- [ ] `getColumns` 等工具函数通过参数接收 `formatMessage`
+- [ ] 语言包中无重复 key
+- [ ] `withLocale` 引用路径正确(组件在子目录使用 `../withLocale`)
+
+### 5. 最后检查
+运行命令找到所有使用 useIntl 的文件,确保都已正确包裹:
+```bash
+grep -r "useIntl" src/ --include="*.js" -l
+```
diff --git "a/prompts/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md" "b/prompts/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md"
new file mode 100644
index 0000000..f6117f6
--- /dev/null
+++ "b/prompts/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md"
@@ -0,0 +1,103 @@
+# 为React组件库添加TypeScript类型定义的通用提示词
+
+## 任务描述
+
+为一个已有的React组件库添加完整的TypeScript类型定义,包括所有组件的Props接口、类型声明和导出。
+
+## 实施步骤
+
+### 1. 创建类型定义文件
+
+在源代码目录创建`index.d.ts`文件,位置与主入口文件`index.js`同级。
+
+### 2. 分析组件结构
+
+- 查看所有组件文件的Props参数
+- 确定每个组件的输入属性类型
+- 识别回调函数的参数和返回值类型
+
+### 3. 定义基础类型
+
+```typescript
+import { ReactNode, ComponentType, FC } from 'react';
+```
+
+### 4. 为每个组件创建Props接口
+
+- 组件名 + Props 作为接口名(如:FormInfoProps)
+- 所有可选属性使用`?`标记
+- ReactNode类型用于支持字符串和JSX元素
+- 函数类型明确定义参数和返回值
+- 使用`[key: string]: any`支持动态属性
+
+### 5. 处理复杂场景
+
+- **嵌套对象属性**:定义内联对象类型
+- **函数属性**:明确参数类型和返回值类型
+- **联合类型**:使用`|`支持多种类型
+- **泛型组件**:使用ComponentType
+
+### 6. 导出组件类型声明
+
+```typescript
+export declare const ComponentName: FC;
+```
+
+## 类型定义模板
+
+### 基础组件Props模板
+
+```typescript
+export interface ComponentNameProps {
+ className?: string;
+ children?: ReactNode;
+
+ // 其他属性...
+ [key: string]: any; // 支持动态属性
+}
+```
+
+### 函数回调模板
+
+```typescript
+onEvent ? : (data: any, context: any, ...args: any[]) => Promise | any;
+```
+
+### 组件声明模板
+
+```typescript
+export declare const ComponentName: FC;
+```
+
+## 最佳实践
+
+1. **类型完整性**:覆盖所有组件的公开API
+2. **向后兼容**:保持现有JavaScript代码的正常运行
+3. **可选属性**:合理使用可选属性,提供良好的开发体验
+4. **文档注释**:为复杂类型添加JSDoc注释
+5. **版本控制**:在package.json中指定types字段指向类型文件
+
+## 使用场景
+
+- 为现有组件库添加TypeScript支持
+- 提升代码可维护性和开发体验
+- 支持IDE智能提示和类型检查
+- 便于团队协作和代码审查
+
+## 示例项目结构
+
+```
+src/
+├── index.js # 主入口文件
+├── index.d.ts # 类型定义文件(新增)
+├── ComponentA.js # 组件A
+├── ComponentB.js # 组件B
+└── ...
+```
+
+## 注意事项
+
+- 类型定义文件应与实际组件实现保持同步
+- 考虑使用更具体的类型替代`any`类型
+- 定期检查和更新类型定义以匹配组件API的变化
+- 对于复杂的组件,考虑拆分类型定义到多个文件中
\ No newline at end of file
diff --git "a/prompts/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md" "b/prompts/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md"
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ "b/prompts/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md"
@@ -0,0 +1,179 @@
+# 生成包功能描述文件(package-manifest.json)的通用提示词
+
+## 任务描述
+
+为一个JavaScript/TypeScript包项目生成`package-manifest.json`文件,该文件用于描述包的主要功能、导出模块、组件API等详细信息。
+
+## 实施步骤
+
+### 1. 分析项目结构
+
+- 查看`package.json`了解包的基本信息
+- 分析主入口文件(`index.js`/`index.ts`)的导出内容
+- 检查源代码目录结构,识别所有可导出的模块/组件
+
+### 2. 识别导出模块
+
+- 默认导出:通常标记为`default`
+- 命名导出:所有具名导出的组件、函数、类等
+
+### 3. 分析每个模块的功能
+
+对于每个导出的模块,需要收集以下信息:
+
+#### React组件
+- **description**: 组件的主要功能描述
+- **type**: 通常为`ReactNode`
+- **props**: 组件的所有属性,包括:
+ - 属性名
+ - 属性描述
+ - 属性类型(支持联合类型,如`string|number`,不能使用下面`类型定义规范`定义之外的类型)
+
+#### 函数/类
+- **description**: 功能描述
+- **type**: 返回值类型
+- **parameters**: 参数列表(如果有)
+- **properties**: 类属性(如果是类)
+
+#### 常量/对象
+- **description**: 用途描述
+- **type**: 数据类型
+- **properties**: 对象属性结构
+
+### 4. 构建JSON结构
+
+```json
+{
+ "description": "包的主要导出模块入口",
+ "modules": {
+ "模块名": {
+ "description": "模块功能描述",
+ "type": "类型声明",
+ // 根据模块类型可能包含以下字段
+ "props": {}, // React组件属性
+ "parameters": [], // 函数参数
+ "properties": {}, // 对象/类属性
+ "returns": "返回值类型" // 函数返回值
+ }
+ }
+}
+```
+
+## 类型定义规范
+
+### 基础类型
+- `string`: 字符串
+- `number`: 数字
+- `boolean`: 布尔值
+- `object`: 对象
+- `array`: 数组
+- `function`: 函数
+- `Date`: 日期对象
+- `Promise`: Promise对象
+- `RegExp`: 正则表达式
+- `Error`: 错误对象
+- `undefined`: 未定义
+- `null`: 空值
+- `any`: 任意类型
+- `void`: 无返回值
+- `unknown`: 未知类型
+- `ReactNode`: React节点(组件专用)
+
+### 联合类型
+使用`|`连接多种类型:
+- `string|ReactNode`: 字符串或React节点
+- `number|object`: 数字或对象
+- `function|null`: 函数或null
+
+### 复杂类型
+- `object[]`: 对象数组
+- `ReactNode[]`: React节点数组
+- `string[]`: 字符串数组
+
+## 优先级排序
+
+1. **主要组件**: 包中最核心、最常用的组件
+2. **辅助组件**: 支持性组件
+3. **工具函数**: 纯函数、工具方法
+4. **类型定义**: TypeScript类型、接口
+5. **常量配置**: 配置对象、常量
+
+## 属性命名约定
+
+### 通用属性
+- `className`: CSS类名
+- `children`: 子元素
+- `style`: 样式对象
+- `title`: 标题
+- `description`: 描述
+
+## 描述编写原则
+
+1. **简洁明确**: 一句话说明核心功能
+2. **用户视角**: 从使用者的角度描述
+3. **突出价值**: 说明解决了什么问题
+4. **避免实现细节**: 不描述内部实现逻辑
+
+## 示例格式
+
+### React组件示例
+```json
+{
+ "FormModal": {
+ "description": "模态框表单组件,在弹窗中展示表单",
+ "type": "ReactNode",
+ "props": {
+ "open": {
+ "description": "是否打开模态框",
+ "type": "boolean"
+ },
+ "title": {
+ "description": "模态框标题",
+ "type": "ReactNode"
+ },
+ "onSubmit": {
+ "description": "提交回调函数",
+ "type": "function"
+ }
+ }
+ }
+}
+```
+
+### 工具函数示例
+```json
+{
+ "formatDate": {
+ "description": "格式化日期字符串",
+ "type": "string",
+ "parameters": [
+ {
+ "name": "format",
+ "description": "格式化模板",
+ "type": "string"
+ }
+ ]
+ }
+}
+```
+
+## 文件位置
+
+将生成的`package-manifest.json`文件放置在:
+- `src/package-manifest.json`
+
+## 使用场景
+
+- 自动化API文档生成
+- IDE智能提示和自动补全
+- 包管理和分发平台的组件展示
+- 团队协作时的API参考
+- 第三方工具的集成和分析
+
+## 注意事项
+
+- 避免包含内部实现细节
+- 不要包含私有方法和属性
+- 确保向后兼容性
+- 定期更新以匹配代码变更
+- 考虑国际化需求(如需要)
\ No newline at end of file
diff --git "a/prompts/\347\224\237\346\210\220\346\226\207\346\241\243.md" "b/prompts/\347\224\237\346\210\220\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..b02891b
--- /dev/null
+++ "b/prompts/\347\224\237\346\210\220\346\226\207\346\241\243.md"
@@ -0,0 +1,79 @@
+# 任务:生成项目文档
+
+## 目标
+根据代码实现生成完整的项目文档,包括项目概述文件 (`doc/summary.md`) 和 API 文档 (`doc/api.md`)。
+
+## 文档生成要求
+
+### doc/summary.md - 项目概述
+#### 格式要求
+- **禁止使用 h1、h2 标题**:直接从内容开始编写
+- **无需标题**:不需要"项目概述"等标题文字
+- **不包含依赖项说明**:专注于项目特点和功能介绍
+- **吸引用户**:突出项目优势和使用价值
+
+#### 内容结构
+1. **项目简介**:简明扼要介绍项目的主要功能和定位
+2. **核心特性**:列出3-5个最突出的特点或优势
+3. **适用场景**:描述适用的业务场景和使用场景
+4. **技术亮点**:重点说明技术创新或设计优势
+
+#### 写作风格
+- **简洁明了**:用通俗语言表达,避免过度技术化
+- **用户导向**:从使用者角度描述价值
+- **重点突出**:强调解决什么问题,带来什么便利
+
+### doc/api.md - API 文档
+#### 格式要求
+- **无需标题**:不添加"API文档"等标题
+- **使用 h3 及以下级别**:组件标题用 h3,子部分用 h4、h5
+- **优先使用表格格式**:API 参数和属性用表格展示
+- **不包含示例代码**:专注于 API 说明,不提供代码示例
+
+#### 内容结构
+```markdown
+### 组件名称
+组件功能描述文字
+
+#### 属性说明
+| 属性名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| prop1 | string | 是 | - | 属性说明 |
+| prop2 | number | 否 | 0 | 属性说明 |
+
+#### 方法说明
+| 方法名 | 参数 | 返回值 | 说明 |
+|--------|------|--------|------|
+| method1 | data: object | void | 方法说明 |
+```
+
+#### API 文档要点
+- **完整性**:涵盖所有公开的 API
+- **准确性**:类型、必填项、默认值要准确
+- **清晰性**:说明文字简洁明确
+- **一致性**:命名和格式保持一致
+
+## 生成流程
+
+### 1. 分析代码结构
+- 阅读 `src/index.js` 了解导出的组件
+- 分析组件的 TypeScript 类型定义 (`src/index.d.ts`)
+- 理解组件的属性和功能
+
+### 2. 提取 API 信息
+- 从组件代码中提取 props 定义
+- 识别方法的参数和返回值
+- 确定属性的类型和默认值
+
+### 3. 编写文档内容
+- 按照格式要求编写两个文档
+- 确保内容准确性和完整性
+- 检查格式规范符合要求
+
+## 严格注意事项
+- **禁止使用 h1、h2 标签**:严格遵守 markdown 标题级别限制
+- **summary.md 无标题**:直接从内容开始
+- **api.md 无文档标题**:直接从第一个组件 h3 标题开始
+- **使用表格格式**:API 说明必须使用表格
+- **不包含示例代码**:专注于文档说明
+- **保持格式一致性**:确保所有文档格式符合要求
\ No newline at end of file
diff --git "a/prompts/\347\224\237\346\210\220\350\257\255\350\250\200\345\214\205.md" "b/prompts/\347\224\237\346\210\220\350\257\255\350\250\200\345\214\205.md"
new file mode 100644
index 0000000..e583148
--- /dev/null
+++ "b/prompts/\347\224\237\346\210\220\350\257\255\350\250\200\345\214\205.md"
@@ -0,0 +1,125 @@
+# 任务:生成和更新国际化语言包
+
+## 目标
+从代码中抽取中文文案,生成完整的国际化语言包,并同步更新到英文语言包。
+
+## 任务要求
+
+### 1. 抽取中文文案
+#### 扫描范围
+- **组件文件**:扫描 `src/` 目录下的所有 `.js` 组件文件
+- **模板字符串**:提取模板字符串中的中文文本
+- **字符串字面量**:提取字符串常量中的中文内容
+- **JSX 文本**:提取 JSX 中的中文显示文本
+
+#### 抽取规则
+- **纯中文文本**:只提取包含中文字符的字符串
+- **过滤无关内容**:忽略 CSS 类名、技术术语、调试信息
+- **保持原意**:确保提取的文案保持原始含义
+- **去重处理**:相同文案只保留一个 key
+
+### 2. 生成语言包结构
+#### zh-CN.js 格式
+```javascript
+const locale = {
+ // 已有的 key 保持不变
+ submit: '提交',
+ cancel: '取消',
+
+ // 新增的 key
+ addText: '添加',
+ deleteText: '删除',
+ // ... 其他新增文案
+};
+
+export default locale;
+```
+
+#### Key 命名规范
+- **驼峰命名**:使用 camelCase 命名规则
+- **语义化**:key 名称要能表达文案含义
+- **简洁明了**:避免过长的 key 名称
+- **一致性**:相似功能使用相同命名模式
+
+### 3. 翻译到英文版本
+#### 翻译原则
+- **准确翻译**:确保英文翻译准确传达中文含义
+- **自然表达**:使用符合英文习惯的表达方式
+- **保持一致**:相同概念使用一致的英文词汇
+- **简洁明了**:避免冗长复杂的表达
+
+#### en-US.js 格式
+```javascript
+const locale = {
+ // 与 zh-CN.js 对应的英文翻译
+ submit: 'Submit',
+ cancel: 'Cancel',
+ complete: 'Complete',
+ next: 'Next',
+ addText: 'Add',
+ deleteText: 'Delete',
+ // ... 其他对应的英文文案
+};
+
+export default locale;
+```
+
+## 实施步骤
+
+### 第一步:代码扫描
+1. 遍历 `src/` 目录下的所有组件文件
+2. 使用正则表达式匹配中文文本
+3. 收集所有中文文案并去重
+4. 过滤掉不需要国际化的内容
+
+### 第二步:更新中文语言包
+1. 打开 `src/locale/zh-CN.js`
+2. 保留现有的 key-value 对
+3. 添加新发现的文案和对应的 key
+4. 确保格式正确,导出语句完整
+
+### 第三步:更新英文语言包
+1. 打开 `src/locale/en-US.js`
+2. 为所有中文 key 添加对应的英文翻译
+3. 确保翻译准确、自然、一致
+4. 保持与中文版本相同的 key 结构
+
+### 第四步:质量检查
+1. **完整性检查**:确保中文和英文版本 key 一致
+2. **准确性检查**:验证翻译的准确性
+3. **格式检查**:确保文件格式符合要求
+4. **语法检查**:确保 JavaScript 语法正确
+
+## 严格注意事项
+- **不修改原始文件**:绝对不要修改任何组件源代码
+- **保留现有内容**:不要删除或修改已有的语言包内容
+- **保持文件结构**:维持两个语言包文件的完整结构
+- **确保导出正确**:保持 `export default locale` 语句
+- **格式一致性**:确保代码格式和缩进一致
+
+## 常见中文文案类型
+- **按钮文本**:确认、取消、添加、删除、保存等
+- **提示信息**:成功、失败、警告、信息提示等
+- **表单标签**:用户名、密码、邮箱、手机号等
+- **操作描述**:编辑、查看、删除、复制等
+- **状态说明**:加载中、已完成、进行中等
+- **错误信息**:必填项验证、格式错误、网络错误等
+
+## 输出格式示例
+```javascript
+// zh-CN.js
+const locale = {
+ // 原有内容
+ submit: '提交',
+ cancel: '取消',
+
+ // 新增内容
+ confirmDelete: '确认删除',
+ deleteSuccess: '删除成功',
+ addNewItem: '添加新项目',
+ loading: '加载中...',
+ required: '此字段为必填项'
+};
+
+export default locale;
+```
\ No newline at end of file
diff --git "a/prompts/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md" "b/prompts/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md"
new file mode 100644
index 0000000..74c5b64
--- /dev/null
+++ "b/prompts/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md"
@@ -0,0 +1,299 @@
+# 组件示例编写提示词
+
+本提示词用于编写遵循特定目录结构规范的 React 项目组件示例。
+
+## 1. 目录结构规范
+
+### 项目目录布局
+
+```
+{PROJECT_ROOT}/
+├── src/ # 源代码目录
+│ └── components/ # 组件源代码
+│ ├── ComponentA/ # 单个组件目录
+│ ├── ComponentB/ # 带子组件的目录
+│ │ ├── Sub1/
+│ │ ├── Sub2/
+│ │ └── index.js
+│ └── ...
+├── doc/ # 文档及示例目录(所有组件共用)
+│ ├── example.json # 示例配置文件(核心)
+│ ├── api.md # 组件API文档
+│ ├── summary.md # 组件简要描述
+│ ├── base.js # 基础示例代码
+│ ├── component-a.js # ComponentA示例
+│ ├── component-b.js # ComponentB主组件示例
+│ ├── component-b-sub1.js # ComponentB子组件Sub1示例
+│ ├── component-b-sub2.js # ComponentB子组件Sub2示例
+│ └── style.scss # 示例样式(可选)
+└── package.json # 项目配置文件
+```
+
+### 示例文件规则
+
+**重要**:所有组件的示例文件都统一放在项目根目录的 `doc/` 中,而不是在每个组件目录下创建单独的 `doc/` 目录。
+
+- 单个组件:使用组件名的小写短横线形式命名示例文件,如 `component-a.js`
+- 带子组件的组件:主组件示例使用组件名,子组件示例使用组件名加子组件名,如 `component-b.js`、`component-b-sub1.js`
+
+### 项目配置变量
+
+在编写示例时需要替换以下变量:
+
+| 变量名 | 说明 | 示例值 |
+|--------|------|--------|
+| `{PACKAGE_NAME}` | 从 package.json 的 name 字段获取 | `@kne/react-file` |
+| `{PROJECT_NAME}` | 项目名称,用于 getPublicPath | `react-file` |
+| `{LIB_NAME}` | 组件库名称,通常是驼峰形式 | `ReactFile` |
+
+## 2. example.json 配置结构
+
+所有组件的示例配置统一在 `doc/example.json` 中管理:
+
+```json
+{
+ "isFull": true,
+ "list": [
+ {
+ "title": "ComponentA",
+ "description": "组件A的描述",
+ "code": "./component-a.js",
+ "scope": [
+ {
+ "name": "_{LIB_NAME}",
+ "packageName": "{PACKAGE_NAME}",
+ "importStatement": "import * as _{LIB_NAME} from \"{PACKAGE_NAME}\""
+ },
+ {
+ "packageName": "{PACKAGE_NAME}/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ },
+ {
+ "title": "ComponentB",
+ "description": "组件B主组件",
+ "code": "./component-b.js",
+ "scope": []
+ },
+ {
+ "title": "ComponentB-Sub1",
+ "description": "组件B子组件Sub1",
+ "code": "./component-b-sub1.js",
+ "scope": []
+ }
+ ]
+}
+```
+
+**重要说明**:
+- `packageName` 可以使用当前组件包名的特殊格式:`@kne/current-lib_{PACKAGE_NAME_SUFFIX}`
+- 例如:`@kne/react-file` 项目可以使用 `@kne/current-lib_react-file` 来引用当前正在开发的组件包
+- 这种特殊写法用于文档示例系统自动解析当前包名
+
+## 3. 示例代码规范
+
+### 导入方式
+
+- 使用 `scope` 中声明的变量名:`const { FilePreview } = _{LIB_NAME};`
+- 工具包引用:`const { createWithRemoteLoader, getPublicPath } = remoteLoader;`
+
+### 标准示例模板(需要 PureGlobal 模拟数据)
+
+```javascript
+const { YourComponent } = _{LIB_NAME};
+const { createWithRemoteLoader, getPublicPath } = remoteLoader;
+
+const BaseExample = createWithRemoteLoader({
+ modules: ['components-core:Global@PureGlobal', 'components-core:InfoPage']
+})(({ remoteModules }) => {
+ const [PureGlobal, InfoPage] = remoteModules;
+ return (
+ {
+ return { data: { code: 0, data: api.loader() } };
+ },
+ apis: {
+ file: {
+ staticUrl: getPublicPath('{PROJECT_NAME}') || window.PUBLIC_URL,
+ getUrl: {
+ loader: async ({ params }) => {
+ // 模拟数据
+ return 'mock-url';
+ }
+ }
+ }
+ }
+ }}>
+
+
+
+
+
+
+ );
+});
+
+render();
+```
+
+### 简化示例模板(无需远程加载)
+
+```javascript
+const { YourComponent } = _{LIB_NAME};
+const { Flex, Switch } = antd;
+const { useState } = React;
+
+const BaseExample = () => {
+ const [state, setState] = useState(false);
+ return (
+
+
+
+ );
+};
+
+render();
+```
+
+## 4. scope 依赖声明规则
+
+| 场景 | name | packageName | importStatement(可选) |
+|------|------|-------------|------------------------|
+| 项目组件 | `_{LIB_NAME}` | `{PACKAGE_NAME}` 或 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}` | `import * as _{LIB_NAME} from "{PACKAGE_NAME}"` |
+| 样式文件 | - | `{PACKAGE_NAME}/dist/index.css` 或 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}/dist/index.css` | - |
+| 远程加载器 | `remoteLoader` | `@kne/remote-loader` | - |
+| Antd组件 | `antd` | `antd` | - |
+| React原生 | `React` | - | -(无需声明) |
+
+**当前包引用规则**:
+- 在 `example.json` 的 `scope` 中引用当前正在开发的组件包时,使用 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}`
+- 例如:`@kne/info-page` 项目应使用 `@kne/current-lib_info-page`
+- 这样可以让文档系统自动识别并加载当前开发中的组件版本
+
+## 5. API文档编写
+
+`doc/api.md` 中包含所有组件的API文档,使用表格格式:
+
+```markdown
+### 组件名称
+
+组件描述
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------|------|-------|------|
+| propName | string | - | 属性描述 |
+```
+
+## 6. 数据模拟
+
+在示例中使用 PureGlobal 的 preset 模拟数据:
+
+```javascript
+apis: {
+ file: {
+ staticUrl: getPublicPath('{PROJECT_NAME}') || window.PUBLIC_URL,
+ getUrl: {
+ loader: async ({ params }) => {
+ // 根据 params 返回对应的 mock 数据
+ const urlMap = {
+ 1: '/mock/example.png',
+ 2: '/mock/example.pdf'
+ };
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(urlMap[params.id]);
+ }, 500);
+ });
+ }
+ }
+ }
+}
+```
+
+## 7. 组件导出规范
+
+在组件的主入口 `index.js` 中,需要导出所有子组件:
+
+```javascript
+export { default } from './MainComponent';
+export { default as SubComponent1 } from './SubComponent1';
+export { default as SubComponent2 } from './SubComponent2';
+```
+
+在示例中通过解构引用:
+
+```javascript
+const { MainComponent, SubComponent1, SubComponent2 } = _{LIB_NAME};
+```
+
+## 8. 使用说明
+
+### 创建新组件示例步骤
+
+1. 在 `src/components/` 下创建或编辑组件
+2. 在 `doc/example.json` 中添加示例配置
+3. 在 `doc/` 目录下创建对应的 `.js` 示例文件
+4. 在 `doc/api.md` 中添加组件API文档(如果是新组件)
+5. 在组件的 `index.js` 中确保正确导出
+
+### 变量替换示例
+
+假设项目 `package.json` 中配置为:
+
+```json
+{
+ "name": "@your-org/your-project"
+}
+```
+
+则变量值为:
+
+- `{PACKAGE_NAME}` → `@your-org/your-project`
+- `{PROJECT_NAME}` → `your-project`
+- `{LIB_NAME}` → `YourProject`
+- `{PACKAGE_NAME_SUFFIX}` → `your-project`(用于当前包引用)
+
+实际使用时需要替换为:
+
+```json
+{
+ "name": "_YourProject",
+ "packageName": "@your-org/your-project",
+ "importStatement": "import * as _YourProject from \"@your-org/your-project\""
+}
+```
+
+**引用当前开发包时的特殊写法**:
+```json
+{
+ "name": "_YourProject",
+ "packageName": "@kne/current-lib_your-project",
+ "importStatement": "import * as _YourProject from \"@your-org/your-project\""
+}
+```
+
+```javascript
+const { YourComponent } = _YourProject;
+const { createWithRemoteLoader, getPublicPath } = remoteLoader;
+
+// ...
+staticUrl: getPublicPath('your-project') || window.PUBLIC_URL,
+```
+
+## 9. 项目参考
+
+本项目的示例文件位于 `doc/` 目录:
+
+- `doc/example.json` - 所有组件的示例配置
+- `doc/api.md` - 所有组件的API文档
+- `doc/summary.md` - 项目概述和核心特性说明
\ No newline at end of file
diff --git a/src/index.d.ts b/src/index.d.ts
new file mode 100644
index 0000000..102138b
--- /dev/null
+++ b/src/index.d.ts
@@ -0,0 +1,224 @@
+import { ReactNode, ComponentType, CSSProperties, HTMLAttributes } from 'react';
+
+/**
+ * Scope项定义,用于向示例代码中注入组件和模块
+ */
+export interface ScopeItem {
+ /**
+ * 组件对象或值
+ */
+ component?: any;
+
+ /**
+ * 在示例代码中使用的变量名
+ */
+ name?: string;
+
+ /**
+ * 包名或模块名,用于生成导入语句
+ */
+ packageName?: string;
+
+ /**
+ * 自定义导入语句,如果提供则不自动生成
+ */
+ importStatement?: string;
+}
+
+/**
+ * LiveCode组件的属性
+ */
+export interface LiveCodeProps {
+ /**
+ * 示例代码字符串
+ */
+ code: string;
+
+ /**
+ * 作用域数组,包含注入到示例代码中的组件和模块
+ */
+ scope: ScopeItem[];
+
+ /**
+ * 示例标题
+ */
+ title: string;
+
+ /**
+ * 示例描述,支持HTML格式
+ */
+ description: string;
+
+ /**
+ * 自定义上下文组件,用于包裹渲染的示例内容
+ */
+ contextComponent?: ComponentType<{ children?: ReactNode }>;
+}
+
+/**
+ * MiniCode组件的属性
+ */
+export interface MiniCodeProps {
+ /**
+ * 示例代码字符串
+ */
+ code: string;
+
+ /**
+ * 二维码图片URL,用于移动端预览
+ */
+ qrcodeUrl: string;
+
+ /**
+ * 作用域数组,包含注入到示例代码中的组件和模块
+ */
+ scope: ScopeItem[];
+
+ /**
+ * 示例标题
+ */
+ title: string;
+
+ /**
+ * 示例描述,支持HTML格式
+ */
+ description: string;
+}
+
+/**
+ * 示例项属性,可以是LiveCode或MiniCode
+ */
+export interface ExampleItem {
+ /**
+ * 示例标题
+ */
+ title: string;
+
+ /**
+ * 示例描述,支持HTML格式
+ */
+ description: string;
+
+ /**
+ * 示例代码
+ */
+ code: string;
+
+ /**
+ * 作用域数组
+ */
+ scope: ScopeItem[];
+
+ /**
+ * 二维码图片URL,如果提供则使用MiniCode模式
+ */
+ qrcodeUrl?: string;
+
+ /**
+ * 自定义上下文组件
+ */
+ contextComponent?: ComponentType<{ children?: ReactNode }>;
+}
+
+/**
+ * DriverItem组件的属性(内部使用)
+ */
+export interface DriverItemProps {
+ /**
+ * 是否全宽显示
+ */
+ isFull?: boolean;
+
+ /**
+ * 自定义上下文组件
+ */
+ contextComponent?: ComponentType<{ children?: ReactNode }>;
+
+ /**
+ * 示例列表
+ */
+ list: ExampleItem[];
+}
+
+/**
+ * Monaco编辑器配置选项
+ */
+export interface MonacoConfigOptions {
+ /**
+ * 模块路径配置
+ */
+ paths?: Record;
+
+ /**
+ * 其他配置选项
+ */
+ [key: string]: any;
+}
+
+/**
+ * Monaco编辑器配置函数
+ */
+export interface MonacoConfig {
+ /**
+ * 配置Monaco编辑器
+ * @param options 配置选项
+ */
+ (options: MonacoConfigOptions): void;
+}
+
+/**
+ * ExampleDriver组件的属性
+ */
+export interface ExampleDriverProps extends HTMLAttributes {
+ /**
+ * 示例列表数组
+ */
+ list: ExampleItem[];
+
+ /**
+ * 是否全宽显示,true时单列显示,false时双列显示
+ * 默认:false
+ */
+ isFull?: boolean;
+
+ /**
+ * 自定义上下文组件,用于包裹所有示例的渲染内容
+ */
+ contextComponent?: ComponentType<{ children?: ReactNode }>;
+
+ /**
+ * 自定义CSS类名
+ */
+ className?: string;
+
+ /**
+ * 其他HTML div元素属性
+ */
+ [key: string]: any;
+}
+
+/**
+ * LiveCode组件声明
+ */
+export declare const LiveCode: ComponentType;
+
+/**
+ * MiniCode组件声明
+ */
+export declare const MiniCode: ComponentType;
+
+/**
+ * DriverItem组件声明
+ */
+export declare const DriverItem: ComponentType;
+
+/**
+ * ExampleDriver主组件
+ * 用于展示和编辑React组件示例
+ */
+export declare const ExampleDriver: ComponentType;
+
+/**
+ * Monaco编辑器配置对象
+ */
+export declare const config: MonacoConfig;
diff --git a/src/index.js b/src/index.js
index cb52e75..c4bfbe7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,26 +1,15 @@
-import React, {useEffect, useRef, useState} from 'react';
-import ReactDOM from "react-dom/client";
+import React, {useEffect, useRef, useState, useMemo, memo} from 'react';
import classnames from 'classnames';
import theme from './theme';
import Highlight, {Prism} from "prism-react-renderer";
-import get from "lodash/get";
+import ErrorBoundary from '@kne/react-error-boundary';
import uniqueId from 'lodash/uniqueId';
import {transform as _transform} from '@babel/standalone';
-import {useDebouncedCallback} from 'use-debounce';
import CodeEditor from '@monaco-editor/react';
import monacoLoader from '@monaco-editor/loader';
+import {useDebouncedCallback} from 'use-debounce';
import './style.scss'
-const renderCallback = (el, callback) => {
- const ProxyComponent = () => {
- useEffect(() => {
- callback();
- }, []);
- return el;
- };
- return ;
-};
-
const HighlightCode = ({code}) => {
return {
};
+const ErrorComponent = memo(({error}) => {
+ return ();
+});
+
const LiveCode = ({code, scope, title, description, contextComponent}) => {
const [_code, setCode] = useState(code), [error, setError] = useState(null), [codeOpen, setCodeOpen] = useState(false), [minHeight, setMinHeight] = useState(0),
runnerRef = useRef(null);
- const currentScope = scope.filter(({component, name}) => !!component && !!name);
- const debounced = useDebouncedCallback((_code) => {
- const runner = runnerRef.current, root = ReactDOM.createRoot(runner),
- beforeHeight = get(runner, 'clientHeight', 0);
- const promise = Promise.resolve()
- .then(() => {
- return _transform(_code, {presets: ['es2015', 'react']}).code;
- })
- .then(runCode => {
- return new Function('React', 'render', ...currentScope.map(({name}) => name), runCode);
- })
- .then(runnerFunction => {
- runnerFunction(React, (customComponent) => {
- const Component = contextComponent || (({children}) => {
- return children;
- });
- root.render({renderCallback(customComponent, () => {
- //只允许预览区域的高度增加,防止在编辑代码的时候预览区域高度反复跳动
- setMinHeight(Math.max(get(runner, 'clientHeight', 0), beforeHeight));
- })});
- }, ...currentScope.map(({component}) => component));
- setError(null);
- })
- .catch(error => {
- setError(error);
- });
- return () => {
- promise.then(() => {
- root.unmount();
- });
- };
- }, 1000);
+ const [renderJsx, setRenderJsx] = useState(null);
+ const [compiledCode, setCompiledCode] = useState(null);
+ const currentScope = useMemo(() => scope.filter(({component, name}) => !!component && !!name), [scope]);
+
+ // 防抖编译函数,避免频繁编译
+ const compileCode = useDebouncedCallback((codeToCompile) => {
+ if (!codeToCompile) {
+ setCompiledCode(null);
+ return;
+ }
+
+ try {
+ setError(null);
+ const transformCode = _transform(codeToCompile, {presets: ['es2015', 'react']}).code;
+ setCompiledCode(transformCode);
+ } catch (e) {
+ setError(e);
+ setCompiledCode(null);
+ }
+ }, 500);
+
+ useEffect(() => {
+ compileCode(_code);
+ }, [_code, compileCode]);
useEffect(() => {
- setCode(code);
- setCodeOpen(false);
- }, [code]);
+ if (!compiledCode) return;
+
+ try {
+ setError(null);
+ // eslint-disable-next-line no-new-func
+ const runnerFunction = new Function('React', 'render', ...currentScope.map(({name}) => name), compiledCode)
+ const Component = contextComponent || (({children}) => {
+ return children;
+ });
+ runnerFunction(React, jsx => setRenderJsx(
+ {jsx}), ...currentScope.map(({component}) => component));
+ } catch (e) {
+ setError(e);
+ }
+ }, [compiledCode, currentScope, contextComponent]);
useEffect(() => {
- return debounced(_code);
- }, [_code]);
+ runnerRef.current && setMinHeight(runnerRef.current.clientHeight);
+ }, [renderJsx]);
+
return <>
{title}
@@ -110,9 +112,7 @@ ${scope.map(({
-
- {error &&
{error.message}}
-
+ {error && }
>) : null}
>;
};
diff --git a/src/package-manifest.json b/src/package-manifest.json
new file mode 100644
index 0000000..201ffc1
--- /dev/null
+++ b/src/package-manifest.json
@@ -0,0 +1,39 @@
+{
+ "description": "用于在线展示和编辑React组件的工具库",
+ "modules": {
+ "default": {
+ "description": "主组件,用于展示和编辑React组件示例,支持LiveCode和MiniCode两种展示模式",
+ "type": "ReactNode",
+ "props": {
+ "list": {
+ "description": "示例列表数组,每个元素代表一个示例",
+ "type": "array"
+ },
+ "isFull": {
+ "description": "是否全宽显示,true时单列显示,false时双列显示,默认双列",
+ "type": "boolean"
+ },
+ "contextComponent": {
+ "description": "自定义上下文组件,用于包裹所有示例的渲染内容",
+ "type": "ReactNode"
+ },
+ "className": {
+ "description": "自定义CSS类名",
+ "type": "string"
+ }
+ }
+ },
+ "config": {
+ "description": "Monaco Editor配置对象,用于自定义Monaco编辑器的加载选项和行为",
+ "type": "function",
+ "parameters": [
+ {
+ "name": "options",
+ "description": "配置选项对象",
+ "type": "object"
+ }
+ ],
+ "returns": "void"
+ }
+ }
+}