diff --git a/package.json b/package.json index 2ec542988..be6fe01dc 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "pnpm": "^8" }, "scripts": { - "preinstall": "npx only-allow pnpm && npm run check-gitmodules", - "check-gitmodules": "test -d packages/graphin/.git || git clone -b v3 https://github.com/antvis/Graphin.git packages/graphin", + "preinstall": "npx only-allow pnpm && npm run add-gitmodule-graphin", + "add-gitmodule-graphin": "test -d packages/graphin/.git || git clone -b v3 https://github.com/antvis/Graphin.git packages/graphin", + "setup:tugraph-db": "node scripts/setupProject.js https://github.com/TuGraph-family/gi-assets-tugraph-db.git packages/gi-assets-tugraph-db", "build:all:es": "turbo run build:es", "build:all:umd": "turbo run build:umd --no-cache", "start": "cd packages/gi-site && npm run start", diff --git a/packages/gi-sdk/docs/Testing.md b/packages/gi-sdk/docs/Testing.md index 0c55de1fc..99d5aabc4 100644 --- a/packages/gi-sdk/docs/Testing.md +++ b/packages/gi-sdk/docs/Testing.md @@ -1,14 +1,102 @@ --- -title: 本地测试 +title: 组件测试 order: 1 group: - path: /quick-start - title: 快速开始 - order: 3 + path: /gi-sdk + title: GISDK + order: 0 nav: title: GISDK path: /sdk order: 1 --- -GISDK_TESTING 介绍 +`@antv/gi-sdk` 提供了 `GISDK_TEST` 组件,用于组件的本地测试,而无需在 G6VP 站点中研发。相比 GISDK 而言,它不用写繁琐的 Config 配置,而是通过资产包的 `registerMeta` 信息自动生成初始化配置,再配合「Setting」的按钮,可视化调整配置,模拟资产在 G6VP 产品中的表现。如下图所示 + +```jsx +import * as React from 'react'; +import { GISDK_TEST } from '@antv/gi-sdk'; +import * as Assets from '@antv/gi-assets-basic'; + +const services = [ + { + id: `GI/PropertiesPanel`, + service: params => { + const { data } = params; + return new Promise(resolve => { + return resolve(data); + }); + }, + }, +]; + +const App = () => { + return ( +
+```jsx | pure
+import GISDK from '@antv/gi-sdk';
+//从 GI 站点上导出的配置
+import { assets, config, services } from './GI_EXPORT_FILES';
+
+const GraphApp = () => {
+ return ;
+};
+
+export default GraphApp;
+```
-但是在少部分情况下,我们需要脱离 G6VP 产品,直接以「写代码」的方式集成。如下图所示:
+我们可以看出,GISDK 仅接受 4 个参数
+
+| Prop Name | Type | Description | Required |
+| ---------- | ---------- | -------------------------------------------- | -------- |
+| `id` | `string` | 唯一标识:用于管理多实例。 | Yes |
+| `config` | `GIConfig` | 配置表:定义节点、边和组件的具体属性。 | Yes |
+| `assets` | `GIAssets` | 资产池:包含节点、边、布局和组件的定义。 | Yes |
+| `services` | `Array` | 服务池:定义了数据获取和其他交互逻辑的函数。 | Yes |
+
+下面,我们将脱离 G6VP 产品,完全构造一个符合上述组件规范的 GISDK DEMO
+
+## DEMO
-## 属性解释
+## 代码解析
+
+在上述的案例中,我们将整个图分析应用划分成 3 部分
+
+- assets:资产池,集合了应用渲染所需的 React 组件
+- config:配置表:每个资产配置的集合,id 用来匹配组件,props 用于运行时参数
+- services:数据池:组件需要数据获取或者其他数据交互
+
+## Assets 资产池!一切都是组件!
+
+如上述案例所示,assets 的数据结构定义如下,这恰好对应了 assets 的三种类型
+
+```jsx | pure
+const assets = {
+ components, //分析资产
+ elements, //元素资产
+ layouts, //元素资产
+};
+```
+
+我们先重点来看 `assets.components` ,以计数器资产为例,是由 `info` `registerMeta` 和 `component` 三部分组成的。
+
+```jsx | pure
+const CounterAsset = {
+ component: Counter,
+ info: {
+ name: '计数器',
+ id: 'Counter',
+ type: 'AUTO',
+ category: 'workbook',
+ },
+ registerMeta: () => ({}),
+};
+```
+
+- `component` 是普通的 React 组件,组件内可以通过 `@antv/gi-sdk` 提供的 `useContext`方法,获得 GISDK 提供的全局状态
+- `registerMeta` 用于 `component` 组件属性 props 的可视化表达, 采用 [Formily](https://www.yuque.com/r/goto?url=https%3A%2F%2Fantd.formilyjs.org%2Fzh-CN%2Fcomponents) 表单库。
+- `info` 是元信息,用于组件的更多信息描述,可以用于 G6VP 站点的上层消费
+
+| 属性 | 类型 | 描述 |
+| --------------- | --------------- | ---------- |
+| `id` | `string` | 资产 ID |
+| `name` | `string` | 资产名称 |
+| `cover` | `string` | 资产缩略图 |
+| `type` | `AssetType` | 资产类型 |
+| `icon` | `string` | 资产 ID |
+| `category` | `AssetCategory` | 资产分类 |
+| `desc` | `string` | 资产描述 |
+| `[key: string]` | `any` | 额外的属性 |
+
+整个 info 元信息中,`id` 和 `type` 是必选的, `id` 默认需要「组件名」「组件文件夹名」保持一致,`type` 决定了资产的运行时渲染逻辑
+
+| AssetType | 名称 | 描述 |
+| -------------- | ---------------- | ---------------------------------------------------------------------------------- |
+| `AUTO` | 自加载组件 | 拥有独立的 UI 和交互,例如 CanvasSetting |
+| `INITIALIZER` | 初始化组件 | 全局唯一,用于初始化 例如 Initializer |
+| `GICC_LAYOUT` | 布局容器组件 | 全局唯一,用于页面布局 例如 SegmentContainer |
+| `GIAC` | 原子组件 | 没有自己的 UI 和交互,都是由容器组件决定的,例如 ZoomIn |
+| `GIAC_CONTENT` | 原子组件(内容) | 有自己的 UI 和交互,但是不能决定自己展示在什么位置,什么时候触发,例如 FilterPanel |
+| `GIAC_MENU` | 原子组件(菜单) | 和 GIAC_CONTENT 类似,但是只能集成在 Menu 容器中 ,例如 RemoveNodeWithMenu |
+| `GICC` | 容器组件 | 提供了容器功能,可以集成 GIAC_CONTENT 也可以集成 GIAC 原子资产。例如 Toolbar |
+| `GICC_MENU` | 容器组件(菜单) | 提供了容器功能,可以集成 GIAC_MENU 原子资产。 例如 ContextMenu |
+| `NODE` | 节点组件 | |
+| `EDGE` | 边组件 | |
+| `LAYOUT` | 布局算法 | |
+
+## Config 配置表!id 用来匹配组件,props 用于运行时参数
+
+`config` 配置表的结构基本上都是`{id:"",props:{}}`,其核心是 SDK 运行时, `id` 用于从资产池中找寻需要运行的资产实例,`props`用于给组件外部传参,(因此 layout 和 pageLayout 其实设计得不是很合理,至少来说是不太一致,后续需要修改)
+
+```jsx | pure
+const config = {
+ nodes: [{ id: 'SimpleNode', props: {} }],
+ edges: [{ id: 'SimpleEdge', props: {} }],
+ components: [
+ { id: 'Counter', props: {} },
+ { id: 'Initializer', props: {} },
+ ],
+ layout: {
+ id: 'Force',
+ props: {},
+ },
+ pageLayout: {},
+};
+```
+
+| 属性 | 类型 | 描述 |
+| ------------ | -------------------------- | ---------------- |
+| `layout` | `GILayoutConfig` | 图的布局配置 |
+| `components` | `Array` | 组件配置数组 |
+| `nodes` | `Array` | 节点配置数组 |
+| `edges` | `Array` | 边配置数组 |
+| `pageLayout` | `GIComponentConfig` | 页面布局组件配置 |
+
+| GIComponentConfig | 类型 | 描述 |
+| ----------------- | ----------- | ------------------ |
+| `id` | `string` | 组件配置的唯一标识 |
+| `name` | `string` | 组件的名称,可选 |
+| `type` | `AssetType` | 资产类型 |
+| `props` | `Object` | 组件的属性配置 |
+
+对于节点和边这类元素资产,我们在`id`和`props`的基础上,增加了`expressions`和`logic`两个关键字段,用于条件渲染,如下述代码所示,`nodes[1]`的逻辑将会先执行,当`id eql account_7 ` 时候,将执行配置`nodes[1].props`
+
+```jsx | pure
+
+ const nodes= [
+ {
+ id: 'SimpleNode',
+ props: {
+ size: 26,
+ color: 'red',
+ label: [],
+ },
+ name: '官方节点',
+ expressions: [],
+ logic: true,
+ groupName: '默认样式',
+ },
+ {
+ id: 'SimpleNode',
+ expressions: [
+ {
+ name: 'id',
+ operator: 'eql',
+ value: 'account_7',
+ },
+ ],
+ props: {
+ size: 26,
+ color: '#3056E3',
+ label: ['id'],
+ },
+ name: '官方节点',
+ logic: true,
+ groupName: 'Account_7 TYPE',
+ },
+ ],
+
+```
+
+| GINodeConfig / GIEdgeConfig | 类型 | 描述 |
+| --------------------------- | --------- | ------------------------ |
+| `id` | `string` | 节点配置的唯一标识 |
+| `name` | `string` | 节点的名称,可选 |
+| `props` | `Object` | 节点的属性配置 |
+| `expressions` | `Array` | 表达式数组,用于条件渲染 |
+| `logic` | `boolean` | 逻辑标识 |
+| `groupName` | `string` | 分组名称 |
+| `default` | `boolean` | 是否为默认设置 |
+
+## Services 服务池!接口别写死,从服务池中匹配捞数据
+
+以上述代码的 `Initializer` 初始化器为例,在组件的运行中,需要查询初始图数据和图模型数据。 对于不同的数据源,比如图数据库,图计算引擎,设置是本地 CSV 文件,数据查询的方式可能不一样,通常做法,我们会这么做
+
+```jsx | pure
+
+```
+
+GI 的设计是这样的,组件的 `props` 设计仅用于静态数据配置,它由 `registerMeta` 产生。对于用户交互,系统交互产生的动态数据,则需要在封装在组件内部,对外仅暴露服务 ID,这样既可以形成接口规范,也有利于服务实现的复写,这对于多引擎的场景下,非常有帮助。如下图所示,不同数据引擎封装了一组资产定义的服务接口,就能够轻松对接上
+
+
+
+```jsx | pure
+
+const services: GIService[] = [
+ {
+ name: '初始化查询',
+ method: 'GET',
+ id: 'GI/GI_SERVICE_INTIAL_GRAPH',
+ service: async () => {
+ return new Promise(resolve => {
+ resolve(data);
+ });
+ },
+ },
+ {
+ name: '查询图模型',
+ method: 'GET',
+ id: 'GI/GI_SERVICE_SCHEMA',
+ service: async () => {
+ return new Promise(resolve => {
+ resolve(schemaData);
+ });
+ },
+ },
+];
+
+const Initializer = props => {
+ const { services, updateContext } = useContext();
+ const { serviceId, schemaServiceId } = props;
+ useEffect(() => {
+ let initialService = services.find(s => s.id === serviceId) as GIService;
+ let schemaService = services.find(s => s.id === schemaServiceId) as GIService;
+
+ Promise.all([schemaService.service(), initialService.service()]).then(([schemaData, graphData]) => {
+ console.log('Initializer', Initializer);
+ updateContext(draft => {
+ draft.data = graphData;
+ draft.schemaData = schemaData;
+ draft.initialized = true;
+ });
+ });
+ }, []);
+ return null;
+};
+
+
+```
diff --git a/packages/gi-sdk/docs/image-1.png b/packages/gi-sdk/docs/image-1.png
new file mode 100644
index 000000000..a57bcb8d0
Binary files /dev/null and b/packages/gi-sdk/docs/image-1.png differ
diff --git a/packages/gi-sdk/docs/image-2.png b/packages/gi-sdk/docs/image-2.png
new file mode 100644
index 000000000..5fa6e2102
Binary files /dev/null and b/packages/gi-sdk/docs/image-2.png differ
diff --git a/packages/gi-sdk/docs/registerContext.md b/packages/gi-sdk/docs/registerContext.md
new file mode 100644
index 000000000..0d44140ac
--- /dev/null
+++ b/packages/gi-sdk/docs/registerContext.md
@@ -0,0 +1,66 @@
+---
+title: registerContext
+order: 2
+group:
+ path: /api
+ title: API
+ order: 3
+nav:
+ title: GISDK
+ path: /sdk
+ order: 1
+---
+
+以内置的 `Canvas` 画布组件为例,它包含了 `apis` 等 6 个变量,`registerContext` 允许子组件将自己的变量注册到全局的 `context` 中,(准确讲叫 ObserveStore 更合理些),从而使得使用它的组件触发重绘
+
+```jsx | pure
+import Graphin from '@antv/graphin';
+import deepClone from 'lodash-es/cloneDeep';
+import React, { useMemo } from 'react';
+import { registerContext, useContext } from './Context';
+
+const initialCanvasStore = {
+ apis: {},
+ HAS_GRAPH: false,
+ data: {
+ nodes: [],
+ edges: [],
+ },
+ source: {
+ nodes: [],
+ edges: [],
+ },
+ schemaData: {
+ nodes: [],
+ edges: [],
+ },
+ renderer: 'canvas', //'webgl-3d',
+};
+registerContext(initialCanvasStore);
+interface CanvasProps {}
+
+const Canvas: React.FunctionComponent = props => {
+ const { context, updateContext, assets, id, updateGraph } = useContext();
+ const { data, nodes, edges, layout, renderer } = context;
+ return (
+
+ );
+};
+
+export default Canvas;
+
+
+```
diff --git a/packages/gi-sdk/docs/typing.md b/packages/gi-sdk/docs/typing.md
new file mode 100644
index 000000000..7051ffb9b
--- /dev/null
+++ b/packages/gi-sdk/docs/typing.md
@@ -0,0 +1,87 @@
+---
+title: 类型参考
+order: 1
+group:
+ path: /gi-sdk
+ title: GISDK
+ order: 0
+nav:
+ title: GISDK
+ path: /sdk
+ order: 1
+---
+
+## AssetInfo
+
+| 属性 | 类型 | 描述 |
+| --------------- | --------------- | ---------- |
+| `id` | `string` | 资产 ID |
+| `name` | `string` | 资产名称 |
+| `cover` | `string` | 资产缩略图 |
+| `type` | `AssetType` | 资产类型 |
+| `icon` | `string` | 资产 ID |
+| `category` | `AssetCategory` | 资产分类 |
+| `desc` | `string` | 资产描述 |
+| `[key: string]` | `any` | 额外的属性 |
+
+整个 info 元信息中,`id` 和 `type` 是必选的, `id` 默认需要「组件名」「组件文件夹名」保持一致,`type` 决定了资产的运行时渲染逻辑
+
+| AssetType | 描述 |
+| -------------------- | ---------------------- |
+| `AUTO` | 自加载组件 initializer |
+| `INITIALIZER` | 初始化组件 |
+| `GICC` | 容器组件,可以多选 |
+| `GICC_LAYOUT` | 布局容器组件, 只能单选 |
+| `GICC_MENU` | 容器组件(菜单) |
+| `GIAC` | 原子组件 |
+| `GIAC_CONTENT` | 原子组件(内容) |
+| `GIAC_MENU` | 原子组件(菜单) |
+| `NODE` | 节点 |
+| `EDGE` | 边 |
+| `LAYOUT` | 布局算法 |
+| `GI_CONTAINER` | 兼容旧版本 |
+| `GI_CONTAINER_INDEX` | 兼容旧版本 |
+
+| AssetCategory | 描述 |
+| ---------------------- | -------- |
+| `container-components` | 容器组件 |
+| `canvas-interaction` | 画布交互 |
+| `elements-interaction` | 元素交互 |
+| `data-analysis` | 数据分析 |
+| `data-query` | 数据查询 |
+| `system-interaction` | 系统交互 |
+| `styling-analysis` | 样式分析 |
+| `algorithm-analysis` | 算法分析 |
+| `workbook` | 工作簿 |
+
+## Config 配置表!id 用来匹配组件,props 用于运行时参数
+
+| 属性 | 类型 | 描述 |
+| ------------ | -------------------------- | ---------------- |
+| `layout` | `GILayoutConfig` | 图的布局配置 |
+| `components` | `Array` | 组件配置数组 |
+| `nodes` | `Array` | 节点配置数组 |
+| `edges` | `Array` | 边配置数组 |
+| `pageLayout` | `GIComponentConfig` | 页面布局组件配置 |
+
+| GIComponentConfig | 类型 | 描述 |
+| ----------------- | ----------- | ------------------ |
+| `id` | `string` | 组件配置的唯一标识 |
+| `name` | `string` | 组件的名称,可选 |
+| `type` | `AssetType` | 资产类型 |
+| `props` | `Object` | 组件的属性配置 |
+
+| GINodeConfig / GIEdgeConfig | 类型 | 描述 |
+| --------------------------- | --------- | ------------------------ |
+| `id` | `string` | 节点配置的唯一标识 |
+| `name` | `string` | 节点的名称,可选 |
+| `props` | `Object` | 节点的属性配置 |
+| `expressions` | `Array` | 表达式数组,用于条件渲染 |
+| `logic` | `boolean` | 逻辑标识 |
+| `groupName` | `string` | 分组名称 |
+| `default` | `boolean` | 是否为默认设置 |
+
+| GILayoutConfig | 类型 | 描述 |
+| -------------- | -------------- | ------------------ |
+| `id` | `string` | 布局配置的唯一标识 |
+| `props` | `LayoutConfig` | 布局的属性配置 |
diff --git a/packages/gi-sdk/docs/updateContext.md b/packages/gi-sdk/docs/updateContext.md
new file mode 100644
index 000000000..d7c37b685
--- /dev/null
+++ b/packages/gi-sdk/docs/updateContext.md
@@ -0,0 +1,41 @@
+---
+title: updateContext
+order: 1
+group:
+ path: /api
+ title: API
+ order: 3
+nav:
+ title: GISDK
+ path: /sdk
+ order: 1
+---
+
+`updateContext` 可以更新全局 `context`
+
+```jsx | pure
+import React from 'react';
+import { useContext } from '@antv/gi-sdk';
+
+const MyComponent = () => {
+ const { context, updateContext } = useContext();
+
+ useEffect(() => {
+ updateContext(preStore => {
+ // 更新数据
+ preStore.data = { nodes: [], edges: [] };
+ // 更新布局
+ preStore.layout = {
+ id: 'Force',
+ props: {},
+ };
+ });
+ // 更新loading
+ updateContext(preStore => {
+ preStore.isLoading = true;
+ });
+ }, []);
+
+ return null;
+};
+```
diff --git a/packages/gi-sdk/docs/useContext.md b/packages/gi-sdk/docs/useContext.md
new file mode 100644
index 000000000..8d56cb00c
--- /dev/null
+++ b/packages/gi-sdk/docs/useContext.md
@@ -0,0 +1,55 @@
+---
+title: useContext
+order: 0
+group:
+ path: /api
+ title: API
+ order: 3
+nav:
+ title: GISDK
+ path: /sdk
+ order: 1
+---
+
+`useContext` 是 GISDK 提供的一个重要方法,它基于 React 的 useContext 和 valtio 库实现。核心用法如下
+
+```jsx | pure
+import React from 'react';
+import { useContext } from '@antv/gi-sdk';
+
+const MyComponent = () => {
+ const {
+ context, // 包含画布状态的快照
+ assets, // 资产信息
+ services, // 服务数组
+ GISDK_ID, // 画布的唯一标识
+ id, // 同 GISDK_ID
+ graph, // IGraph 图形实例
+ updateContext, // 函数,用于更新画布的状态
+ updateGraph, // 函数,用于更新 IGraph 实例
+ } = useContext();
+};
+```
+
+其中,`context` 包含下述这么多变量,这些变量都是 `valtio` 的 `proxy` 对象,这就意味着,只有当你的组件中,消费到这些变量的时候,它才触发重绘
+
+| Key | Type | Description |
+| ----------------- | --------------------- | ------------------------------------------------ |
+| `apis` | `any` | 存储与画布相关的 API 方法。 |
+| `renderer` | `string` | 指示使用的渲染器类型。 |
+| `HAS_GRAPH` | `boolean` | 指示是否已经加载了图形。 |
+| `GISDK_ID` | `string` | 画布的唯一标识。 |
+| `isLoading` | `boolean` | 指示画布是否正在加载数据。 |
+| `data` | `GraphinData` | 存储图形的数据,包括节点和边。 |
+| `source` | `GraphinData` | 存储原始图形数据。 |
+| `schemaData` | `GraphSchemaData` | 存储图形的模式数据。 |
+| `largeGraphLimit` | `number` | 定义大图模式的节点数限制。 |
+| `largeGraphData` | `GraphinData` | 用于大图模式下的图形数据。 |
+| `largeGraphMode` | `boolean` | 指示是否启用大图模式。 |
+| `nodes` | `GINodeConfig[]` | 数组存储节点配置。 `props.config.nodes` |
+| `edges` | `GIEdgeConfig[]` | 数组存储边配置。 `props.config.edges` |
+| `layout` | `GILayoutConfig` | 存储图形布局配置。 `props.config.layout` |
+| `components` | `GIComponentConfig[]` | 数组存储组件配置。 `props.config.components` |
+| `pageLayout` | `any` | 用于存储页面布局配置。 `props.config.pageLayout` |
+| `initialized` | `boolean` | 指示画布是否已初始化。 |
+| `prepare` | `boolean` | 可能用于指示预备状态或前置条件。 |
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index a3c55cd7b..de38107ec 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -2,5 +2,3 @@ packages:
# all packages in subdirs of packages/ and components/
- 'packages/**'
- '!packages/gi-cli/templates'
- - '!packages/gi-assets-tugraph-db'
- - '!packages/gi-assets-xlab'
diff --git a/scripts/setupProject.js b/scripts/setupProject.js
new file mode 100644
index 000000000..fa02b4c4e
--- /dev/null
+++ b/scripts/setupProject.js
@@ -0,0 +1,118 @@
+const fs = require('fs');
+const path = require('path');
+const { exec } = require('child_process');
+
+// 从命令行参数获取 repoUrl 和 cloneDir
+const [, , repoUrl, cloneDirPath] = process.argv;
+
+const cloneDir = path.join(__dirname, '../', cloneDirPath);
+const packageJsonPath = path.join(__dirname, '../', `${cloneDirPath}/package.json`);
+const siteJsonPath = path.join(__dirname, '../', 'packages/gi-site/package.json');
+
+function cloneRepository() {
+ if (fs.existsSync(cloneDir)) {
+ console.log('目录已存在,跳过克隆操作。');
+ return;
+ }
+
+ exec(`git clone ${repoUrl} ${cloneDir}`, (err, stdout, stderr) => {
+ if (err) {
+ console.error('克隆仓库时出错:', err);
+ return;
+ }
+ console.log('仓库克隆成功。');
+ });
+}
+
+function updatePackageJson() {
+ if (!fs.existsSync(packageJsonPath)) {
+ console.log('package.json 文件不存在,跳过更新操作。');
+ return;
+ }
+ let pkgName;
+
+ /** update asset package.json */
+ fs.readFile(packageJsonPath, 'utf8', (err, data) => {
+ if (err) {
+ console.error('读取文件时出错:', err);
+ return;
+ }
+
+ let packageJson;
+
+ try {
+ packageJson = JSON.parse(data);
+ } catch (jsonErr) {
+ console.error('解析 JSON 时出错:', jsonErr);
+ return;
+ }
+
+ const dependencies = packageJson.dependencies;
+ pkgName = packageJson.name;
+ let updated = false;
+ if (dependencies) {
+ if (dependencies['@antv/gi-sdk']) {
+ dependencies['@antv/gi-sdk'] = 'workspace:*';
+ updated = true;
+ }
+ if (dependencies['@antv/graphin']) {
+ dependencies['@antv/graphin'] = 'workspace:*';
+ updated = true;
+ }
+ }
+
+ if (updated) {
+ fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8', writeErr => {
+ if (writeErr) {
+ console.error('写入文件时出错:', writeErr);
+ return;
+ }
+ console.log(`${pkgName} package.json 已更新。`);
+ });
+ } else {
+ console.log(`无需更新 ${pkgName} package.json。`);
+ }
+
+ updateSite(pkgName);
+ });
+}
+/** update asset site.json */
+function updateSite(pkgName) {
+ fs.readFile(siteJsonPath, 'utf8', (err, data) => {
+ if (err) {
+ console.error('读取文件时出错:', err);
+ return;
+ }
+ let packageJson;
+ try {
+ packageJson = JSON.parse(data);
+ } catch (jsonErr) {
+ console.error('解析 JSON 时出错:', jsonErr);
+ return;
+ }
+ const dependencies = packageJson.dependencies;
+
+ let updated = false;
+
+ if (!dependencies[pkgName]) {
+ dependencies[pkgName] = 'workspace:*';
+ updated = true;
+ }
+
+ if (updated) {
+ fs.writeFile(siteJsonPath, JSON.stringify(packageJson, null, 2), 'utf8', writeErr => {
+ if (writeErr) {
+ console.error('写入文件时出错:', writeErr);
+ return;
+ }
+ console.log('site package.json 已更新。');
+ });
+ } else {
+ console.log('无需更新 site package.json。');
+ }
+ });
+}
+
+// 执行脚本
+cloneRepository();
+updatePackageJson();