neo-cmp-cli 1.3.10 → 1.5.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +52 -15
  2. package/package.json +4 -1
  3. package/src/cmpUtils/createCmpByTemplate.js +50 -0
  4. package/src/cmpUtils/createCommonModulesCode.js +15 -15
  5. package/src/cmpUtils/{getCmpModelRegister.js → getCmpModelRegisterCode.js} +2 -2
  6. package/src/cmpUtils/{getCmpPreview.js → getCmpPreviewCode.js} +2 -2
  7. package/src/cmpUtils/{getCmpRegister.js → getCmpRegisterCode.js} +2 -2
  8. package/src/cmpUtils/getCmpTypeByDir.js +41 -0
  9. package/src/cmpUtils/hasCmpTypeByDir.js +11 -0
  10. package/src/{module → cmpUtils}/previewCmp.js +2 -2
  11. package/src/cmpUtils/pushCmp.js +232 -0
  12. package/src/config/default.config.js +14 -2
  13. package/src/module/index.js +174 -11
  14. package/src/module/main.js +96 -3
  15. package/src/module/neoInit.js +3 -0
  16. package/src/module/neoInitByCopy.js +3 -0
  17. package/src/neo/NeoUMDContent.js +29 -0
  18. package/src/neo/neoRequire.js +7 -7
  19. package/src/neo/neoService.js +525 -0
  20. package/src/neo/wrapperContent.js +2 -1
  21. package/src/oss/publish2oss.js +96 -96
  22. package/src/plugins/AddNeoRequirePlugin.js +5 -2
  23. package/src/projectUtils/createCmpProjectByTemplate.js +49 -0
  24. package/src/{cmpUtils → projectUtils}/getEntriesWithAutoRegister.js +4 -4
  25. package/src/template/antd-custom-cmp-template/README.md +2 -2
  26. package/src/template/antd-custom-cmp-template/neo.config.js +22 -14
  27. package/src/template/antd-custom-cmp-template/package.json +2 -2
  28. package/src/template/develop/neo-custom-cmp-template/neo.config.js +1 -1
  29. package/src/template/echarts-custom-cmp-template/README.md +2 -2
  30. package/src/template/echarts-custom-cmp-template/neo.config.js +19 -13
  31. package/src/template/echarts-custom-cmp-template/package.json +2 -2
  32. package/src/template/empty-cmp/index.tsx +51 -0
  33. package/src/template/empty-cmp/model.ts +77 -0
  34. package/src/template/empty-cmp/style.scss +72 -0
  35. package/src/template/empty-custom-cmp-template/.prettierrc.js +12 -0
  36. package/src/template/empty-custom-cmp-template/README.md +45 -0
  37. package/src/template/empty-custom-cmp-template/commitlint.config.js +59 -0
  38. package/src/template/empty-custom-cmp-template/neo.config.js +126 -0
  39. package/src/template/empty-custom-cmp-template/package.json +57 -0
  40. package/src/template/empty-custom-cmp-template/public/css/base.css +283 -0
  41. package/src/template/empty-custom-cmp-template/public/scripts/app/bluebird.js +6679 -0
  42. package/src/template/empty-custom-cmp-template/public/template.html +13 -0
  43. package/src/template/empty-custom-cmp-template/src/assets/css/common.scss +127 -0
  44. package/src/template/empty-custom-cmp-template/src/assets/css/mixin.scss +47 -0
  45. package/src/template/empty-custom-cmp-template/src/assets/img/NeoCRM.jpg +0 -0
  46. package/src/template/empty-custom-cmp-template/src/assets/img/custom-widget.svg +1 -0
  47. package/src/template/empty-custom-cmp-template/src/assets/img/favicon.png +0 -0
  48. package/src/template/empty-custom-cmp-template/src/assets/img/map.svg +1 -0
  49. package/src/template/empty-custom-cmp-template/src/components/README.md +3 -0
  50. package/src/template/empty-custom-cmp-template/tsconfig.json +68 -0
  51. package/src/template/neo-custom-cmp-template/README.md +5 -5
  52. package/src/template/neo-custom-cmp-template/neo.config.js +18 -29
  53. package/src/template/neo-custom-cmp-template/package.json +3 -4
  54. package/src/template/react-custom-cmp-template/.prettierrc.js +1 -1
  55. package/src/template/react-custom-cmp-template/README.md +2 -2
  56. package/src/template/react-custom-cmp-template/neo.config.js +20 -15
  57. package/src/template/react-custom-cmp-template/package.json +2 -2
  58. package/src/template/react-ts-custom-cmp-template/README.md +2 -2
  59. package/src/template/react-ts-custom-cmp-template/neo.config.js +19 -14
  60. package/src/template/react-ts-custom-cmp-template/package.json +2 -2
  61. package/src/template/vue2-custom-cmp-template/README.md +2 -2
  62. package/src/template/vue2-custom-cmp-template/neo.config.js +20 -15
  63. package/src/template/vue2-custom-cmp-template/package.json +2 -2
  64. package/src/utils/autoEntryRootDir.js +42 -0
  65. package/src/utils/replaceInFilesByMap.js +54 -0
  66. package/test/demo.js +2 -2
  67. /package/src/{cmpUtils → projectUtils}/getEntries.js +0 -0
  68. /package/src/{cmpUtils → projectUtils}/updatePublishLog.js +0 -0
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  ## Neo 自定义组件开发工具
2
- neo-cmp-cli 是 Neo 自定义组件开发工具,基于 [AKFun](https://github.com/wibetter/akfun) 的工程能力,提供 初始化、编译构建、预览调试、热更新、多技术栈支持和一键发布等功能。
2
+ neo-cmp-cli 是 Neo 自定义组件开发工具,基于 [AKFun](https://github.com/wibetter/akfun) 的工程能力,提供 初始化、编译构建、预览调试、热更新、多技术栈支持和发布等功能。
3
3
 
4
4
  ### 主要特性
5
5
  - **零配置**: 内置默认配置,开箱可用;
6
6
  - **多技术栈**: 支持 Vue2、React、React+TypeScript 自定义组件的调试、构建与发布;
7
- - **多构建场景**: 本地预览(含热更新/代理)、外链调试、库构建(UMD/ESM);
7
+ - **多构建场景**: 本地预览(含热更新/代理)、外链调试、库构建(UMD/ESM)、部署&发布;
8
8
  - **灵活可配**: 支持 构建入口、别名、代理、SASS 注入、ESLint/StyleLint、Babel/Loader/Plugin 扩展等配置;
9
9
  - **样式与规范**: 内置 Autoprefixer、Sass、PostCSS、ESLint、StyleLint;
10
- - **参数替换**: 支持基于 [params-replace-loader](https://www.npmjs.com/package/params-replace-loader) 的环境变量批量替换;
11
- - **一键发布**: 内置发布到 OSS 的能力,并支持自定义对象存储配置。
10
+ - **发布至 CDN**: 内置发布到对象存储(OSS)的能力,支持自定义对象存储配置;
11
+ - **发布至 NeoCRM 平台**: 支持一键发布到NeoCRM 平台的能力,需自行补充授权配置;
12
12
 
13
13
  ### 内置的自定义组件模板
14
14
  创建自定义组件时(执行初始化命令 neo init)可选用。
@@ -40,8 +40,8 @@ neo preview
40
40
  # 外链调试(在平台线上预览与调试)
41
41
  neo linkDebug
42
42
 
43
- # 构建并发布到 OSS(确保 package.json 的 name 唯一、version 不重复)
44
- neo publish2oss
43
+ # 构建并发布到 NeoCRM(需自行添加授权配置,并确保 package.json 的 name 唯一、version 不重复)
44
+ neo pushCmp
45
45
  ```
46
46
 
47
47
  ### 方法二:在现有业务项目中使用自定义组件开发工具
@@ -58,7 +58,7 @@ npm i neo-cmp-cli --save-dev
58
58
  ```bash
59
59
  "preview": "neo preview",
60
60
  "linkDebug": "neo linkDebug",
61
- "publish2oss": "neo publish2oss"
61
+ "pushCmp": "neo pushCmp"
62
62
  ```
63
63
  ##### 3) 初始化配置文件
64
64
  ```bash
@@ -68,7 +68,7 @@ neo config init
68
68
  ```bash
69
69
  npm run preview
70
70
  npm run linkDebug
71
- npm run publish2oss
71
+ npm run pushCmp
72
72
  ```
73
73
 
74
74
  ## 常用命令说明
@@ -76,10 +76,11 @@ npm run publish2oss
76
76
  - **neo preview**: 本地预览自定义组件内容,默认支持热更新与接口代理。
77
77
  - **neo linkDebug**: 外链调试模式,在平台端页面设计器中调试自定义组件。
78
78
  - **neo publish2oss**: 构建并上传到对象存储(可自定义配置对象存储)。
79
+ - **neo pushCmp**: 构建并发布到NeoCRM平台(需自行添加授权配置)。
79
80
 
80
81
  ## 开发须知
81
82
  #### 1)默认自动识别自定义组件
82
- - **自动生成入口配置**: 当 `entry` 未配置时,自动从 `src/components` 目录下扫描并识别自定义组件,`src/components` 下的子目录名称作为自定义组件的 cmpType,并以其目录下的 `index.ts/.tsx/.js/.jsx` 文件作为组件内容文件,model.[tj]s 作为模型内容文件;
83
+ - **自动生成入口配置**: 当 `entry` 未配置时,自动从 `src/components` 目录下扫描并识别自定义组件,`src/components` 下的子目录名称作为自定义组件的名称,并以其目录下的 `index.ts/.tsx/.js/.jsx` 文件作为组件内容文件,model.[tj]s 作为模型内容文件;
83
84
  - **自动注册自定义组件**: 当 `entry` 未配置时,自动生成自定义组件注册文件和模型注册文件,并注入到构建脚本中,无需用户手动编写注册文件([neo-register](https://www.npmjs.com/package/neo-register))。
84
85
 
85
86
  #### 2)设置自定义组件属性配置项
@@ -128,16 +129,49 @@ neo linkDebug
128
129
  ##### 3. 页面设计器开启 debug 模式后,左侧会展示 外部链接 管理面板
129
130
  将第 1 步生成的「外链脚本地址」添加进来,即可在此页面设计器 / 组件物料面板中看到对应自定义组件。
130
131
 
131
- #### 6)发布自定义组件
132
- 执行 `neo publish2oss` 即可构建对应自定义组件,并自动将构建后资源上传到对象存储(OSS)中。
132
+ #### 6)发布自定义组件至 NeoCRM
133
+ 执行 `neo pushCmp` 即可构建并发布自定义组件至 NeoCRM 平台,其构建后资源也会上传到 NeoCRM 平台端提供的 CDN 中。
133
134
 
134
135
  ##### 发布前请确保
135
136
  - **package.json 的 name 唯一**
136
137
  - **version 不重复**
137
- - 已按需配置对象存储参数(支持自定义)
138
+
139
+ ##### 需自行添加授权配置
140
+ ```javascript
141
+ module.exports = {
142
+ pushCmp: {
143
+ neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
144
+ tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址(默认:https://login.xiaoshouyi.com/auc/oauth2/token)
145
+ // NeoCRM 授权配置
146
+ authConfig: {
147
+ /**
148
+ * 客户端 ID 和 客户端秘钥 需通过 创建连接器 获取,
149
+ * 详细见:https://doc.xiaoshouyi.com / 创建连接器。
150
+ */
151
+ client_id: 'xx', // 客户端 ID,从创建连接器的客户端信息中获取(Client_Id)
152
+ client_secret: 'xxx', // 客户端秘钥,从创建连接器的客户端信息中获取(Client_Secret)
153
+ username: 'xx', // 用户在销售易系统中的用户名
154
+ /**
155
+ * password 为 用户在销售易系统中的账号密码加上 8 位安全令牌。
156
+ * 例如,用户密码为 123456,安全令牌为 ABCDEFGH,则 password 的值应为 123456ABCDEFGH。
157
+ * 如何获取 安全令牌请见:https://doc.xiaoshouyi.com / OAuth安全认证 / 密码模式 / 获取令牌。
158
+ */
159
+ password: 'xx xx' // 用户账户密码 + 8 位安全令牌
160
+ },
161
+ },
162
+ }
163
+ ```
138
164
 
139
165
  ##### 支持发布指定自定义组件
140
- 执行 `neo publish2oss --cmpType=xxCmp`
166
+ 执行 `neo pushCmp --name=xxCmp`
167
+
168
+ #### 7)发布自定义组件至CDN
169
+ 执行 `neo publish2oss` 即可构建对应自定义组件,并自动将构建后资源上传到对象存储(OSS)中。
170
+
171
+ ##### 发布前请确保
172
+ - **package.json 的 name 唯一**
173
+ - **version 不重复**
174
+ - 可按需配置对象存储参数(支持自定义),默认使用内置对象存储配置。
141
175
 
142
176
  ##### 支持自定义对象存储配置
143
177
  ```javascript
@@ -159,6 +193,9 @@ module.exports = {
159
193
  }
160
194
  ```
161
195
 
196
+ ##### 支持发布指定自定义组件
197
+ 执行 `neo publish2oss --name=xxCmp`
198
+
162
199
  ## 项目工程配置说明(neo.config.js)
163
200
  neo-cmp-cli 默认提供完整配置;
164
201
  如需自定义,使用 `neo config init` 生成 `neo.config.js` 并按需修改。
@@ -327,8 +364,8 @@ module.exports = {
327
364
  ```javascript
328
365
  module.exports = {
329
366
  neoCommonModule: {
330
- remotes: ['neo-custom-cmpA'], // 远程自定义组件,表示当前自定义组件 B 会用到的自定义组件
331
- neoExternals: ['xxModule_A'], // 自定义组件中需要剔除的模块(远程自定义组件中分享出来的模块),仅支持数组写法,需要和 remotes 配合使用
367
+ remoteDeps: ['neo-custom-cmpA'], // 远程自定义组件,表示当前自定义组件 B 会用到的自定义组件
368
+ neoExternals: ['xxModule_A'], // 自定义组件中需要剔除的模块(远程自定义组件中分享出来的模块),仅支持数组写法,需要和 remoteDeps 配合使用
332
369
  },
333
370
  }
334
371
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo-cmp-cli",
3
- "version": "1.3.10",
3
+ "version": "1.5.0-beta.10",
4
4
  "description": "前端脚手架:自定义组件开发工具,支持react 和 vue2.0技术栈。",
5
5
  "keywords": [
6
6
  "neo-cli",
@@ -41,10 +41,13 @@
41
41
  "url": "https://github.com/wibetter/neo-cmp-cli/issues"
42
42
  },
43
43
  "dependencies": {
44
+ "adm-zip": "^0.5.10",
44
45
  "akfun": "^5.1.12",
46
+ "axios": "^0.27.2",
45
47
  "chalk": "^4.0.0",
46
48
  "deepmerge": "^4.2.2",
47
49
  "figlet": "^1.2.0",
50
+ "form-data": "^4.0.0",
48
51
  "fs-extra": "^10.1.0",
49
52
  "inquirer": "^7.3.3",
50
53
  "ora": "^4.0.4",
@@ -0,0 +1,50 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const _ = require('lodash');
4
+ const { consoleTag } = require('../utils/neoParams'); // 输出标记
5
+ const replaceInFilesByMap = require('../utils/replaceInFilesByMap');
6
+ const hasCmpTypeByDir = require('./hasCmpTypeByDir');
7
+
8
+ // 自定义组件内容模板信息
9
+ const curCmpTemplate = {
10
+ // 需要替换掉的字段信息(模板中的字段)
11
+ widgetInfo: {
12
+ cmpName: 'CustomCmp',
13
+ modelName: 'CustomCmpModel',
14
+ cmpClassName: 'custom-cmp-container',
15
+ cmpType: 'xx-custom-cmp',
16
+ cmpLabel: 'xx组件',
17
+ },
18
+ dir: path.resolve(__dirname, '../template/empty-cmp')
19
+ };
20
+
21
+ /**
22
+ * 创建自定义组件
23
+ * @param {*} cmpName 自定义组件名称
24
+ */
25
+ module.exports = function (cmpName) {
26
+ const currentTemplateDir = curCmpTemplate.dir;
27
+ const finalCmpName = cmpName || 'neoCustomCmp';
28
+ const finalCmpPath = path.resolve(process.cwd(), finalCmpName);
29
+
30
+ if (hasCmpTypeByDir(cmpType)) {
31
+ console.error(`${consoleTag}创建自定义组件失败,当前已经存在${cmpType}自定义组件。`);
32
+ process.exit(1);
33
+ }
34
+
35
+ fs.copy(currentTemplateDir, finalCmpPath)
36
+ .then(() => {
37
+ const curCmpName = _.camelCase(cmpName);
38
+ const cmpType = _.kebabCase(cmpName);
39
+ replaceInFilesByMap(finalCmpPath, {
40
+ [curCmpTemplate.widgetInfo.cmpName]: curCmpName,
41
+ [curCmpTemplate.widgetInfo.modelName]: `${curCmpName}Model`,
42
+ [curCmpTemplate.widgetInfo.cmpClassName]: `${cmpType}-container`,
43
+ [curCmpTemplate.widgetInfo.cmpType]: cmpType,
44
+ [curCmpTemplate.widgetInfo.cmpLabel]: `${cmpType}组件`,
45
+ });
46
+
47
+ console.log(`${consoleTag}已创建自定义组件(${finalCmpName})!`);
48
+ })
49
+ .catch((err) => console.error(`${consoleTag}自定义组件创建失败(${finalCmpName}):`, err));
50
+ };;
@@ -9,15 +9,15 @@ const { isPlainObject } = require('lodash');
9
9
  * @returns 组件预览代码
10
10
  */
11
11
  const createCommonModulesCode = (neoCommonModule, cmpTypes) => {
12
- const {neoExports, remotes} = neoCommonModule;
12
+ const {neoExports, remoteDeps} = neoCommonModule;
13
13
 
14
- if (!neoExports && !remotes) {
14
+ if (!neoExports && !remoteDeps) {
15
15
  return '';
16
16
  }
17
17
  // 记录当前自定义组件共享出去的模块
18
18
  const CustomCmpCommonModules = {};
19
- // 记录当前自定义组件需要的远程组件
20
- const CustomCmpRemotes = {};
19
+ // 记录当前自定义组件需要的远程依赖组件
20
+ const CustomCmpRemoteDeps = {};
21
21
 
22
22
  // 根据 neoExports 获取共享的依赖模块
23
23
  if (Array.isArray(neoExports) && neoExports.length > 0) {
@@ -35,10 +35,10 @@ const createCommonModulesCode = (neoCommonModule, cmpTypes) => {
35
35
  process.exit(1);
36
36
  }
37
37
 
38
- // 根据 cmpTypes 和 remotes 设置远程组件信息
39
- if (Array.isArray(remotes) && remotes.length > 0) {
38
+ // 根据 cmpTypes 和 remoteDeps 设置远程依赖组件信息
39
+ if (Array.isArray(remoteDeps) && remoteDeps.length > 0) {
40
40
  cmpTypes.forEach((cmpType) => {
41
- CustomCmpRemotes[cmpType] = remotes;
41
+ CustomCmpRemoteDeps[cmpType] = remoteDeps;
42
42
  });
43
43
  }
44
44
 
@@ -58,7 +58,7 @@ const createCommonModulesCode = (neoCommonModule, cmpTypes) => {
58
58
  */
59
59
  import { isPlainObject } from 'lodash';
60
60
  const CustomCmpCommonModules = ${customCmpCommonModulesCode};
61
- const CustomCmpRemotes = ${JSON.stringify(CustomCmpRemotes)};
61
+ const CustomCmpRemoteDeps = ${JSON.stringify(CustomCmpRemoteDeps)};
62
62
 
63
63
  // 用于添加共享的依赖模块
64
64
  const addNeoCommonModules = (modules) => {
@@ -88,21 +88,21 @@ const addNeoCommonModules = (modules) => {
88
88
  }
89
89
  }
90
90
 
91
- // 用于添加自定义组件的远程组件(关联使用)
92
- const addNeoRemotes = (remotes) => {
91
+ // 用于添加自定义组件的远程依赖组件(关联使用)
92
+ const addNeoRemoteDeps = (remoteDeps) => {
93
93
  if (!window.__NeoCommonModules) {
94
94
  window.__NeoCommonModules = {}
95
95
  }
96
- if (!window.__NeoCommonModules.__neoRemotes) {
97
- window.__NeoCommonModules.__neoRemotes = {}
96
+ if (!window.__NeoCommonModules.__neoRemoteDeps) {
97
+ window.__NeoCommonModules.__neoRemoteDeps = {}
98
98
  }
99
- if (isPlainObject(remotes)) {
100
- window.__NeoCommonModules.__neoRemotes = Object.assign(window.__NeoCommonModules.__neoRemotes, remotes)
99
+ if (isPlainObject(remoteDeps)) {
100
+ window.__NeoCommonModules.__neoRemoteDeps = Object.assign(window.__NeoCommonModules.__neoRemoteDeps, remoteDeps)
101
101
  }
102
102
  }
103
103
 
104
104
  addNeoCommonModules(CustomCmpCommonModules);
105
- addNeoRemotes(CustomCmpRemotes);
105
+ addNeoRemoteDeps(CustomCmpRemoteDeps);
106
106
  `;
107
107
 
108
108
  // 创建存放 cli 的临时目录
@@ -7,7 +7,7 @@ const { resolveToCurrentRoot } = require('../utils/pathUtils');
7
7
  * @param {*} cmpName 自定义组件名称
8
8
  * @returns 组件注册文件内容
9
9
  */
10
- const getCmpModelRegister = (cmpsDir, cmpName) => {
10
+ const getCmpModelRegisterCode = (cmpsDir, cmpName) => {
11
11
  const cpmModelDir = resolveToCurrentRoot(`${cmpsDir}/${cmpName}/model`);
12
12
 
13
13
  /*
@@ -28,4 +28,4 @@ registerNeoEditorModel(CustomCmpModel, '${cmpName}');
28
28
  `;
29
29
  };
30
30
 
31
- module.exports = getCmpModelRegister;
31
+ module.exports = getCmpModelRegisterCode;
@@ -7,7 +7,7 @@ const { resolveToCurrentRoot } = require('../utils/pathUtils');
7
7
  * @param {*} cmpName 自定义组件名称
8
8
  * @returns 组件预览代码
9
9
  */
10
- const getCmpPreview = (cmpsDir, cmpName) => {
10
+ const getCmpPreviewCode = (cmpsDir, cmpName) => {
11
11
  const cpmDir = resolveToCurrentRoot(`${cmpsDir}/${cmpName}`);
12
12
  const cpmModelDir = resolveToCurrentRoot(`${cmpsDir}/${cmpName}/model`);
13
13
 
@@ -37,4 +37,4 @@ ReactDOM.render(
37
37
  `;
38
38
  };
39
39
 
40
- module.exports = getCmpPreview;
40
+ module.exports = getCmpPreviewCode;
@@ -7,7 +7,7 @@ const { resolveToCurrentRoot } = require('../utils/pathUtils');
7
7
  * @param {*} cmpName 自定义组件名称
8
8
  * @returns 组件注册文件内容
9
9
  */
10
- const getCmpRegister = (cmpsDir, cmpName) => {
10
+ const getCmpRegisterCode = (cmpsDir, cmpName) => {
11
11
  const cpmIndexDir = resolveToCurrentRoot(`${cmpsDir}/${cmpName}/index`);
12
12
 
13
13
  /*
@@ -28,4 +28,4 @@ registerNeoCmp(CustomCmp, '${cmpName}');
28
28
  `;
29
29
  };
30
30
 
31
- module.exports = getCmpRegister;
31
+ module.exports = getCmpRegisterCode;
@@ -0,0 +1,41 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { resolveToCurrentRoot } = require('../utils/pathUtils');
4
+ /**
5
+ * 根据当前组件目录,获取所有组件类型
6
+ * @param {*} componentsBaseDir 自定义组件目录
7
+ * @returns 组件类型列表
8
+ */
9
+ const getCmpTypeByDir = (componentsBaseDir = './src/components') => {
10
+ const componentsDir = resolveToCurrentRoot(componentsBaseDir);
11
+ if (!fs.existsSync(componentsDir)) {
12
+ console.error(`未找到自定义组件目录,请检查 ${componentsDir} 目录是否存在。`);
13
+ // 退出进程
14
+ process.exit(1);
15
+ }
16
+
17
+ try {
18
+ // 读取组件目录下的所有子目录
19
+ const dirs = fs.readdirSync(componentsDir);
20
+ const cmpTypes = [];
21
+
22
+ // 遍历所有目录,过滤出有效的组件类型
23
+ dirs.forEach((dir) => {
24
+ const dirPath = path.join(componentsDir, dir);
25
+ const stat = fs.statSync(dirPath);
26
+
27
+ // 只处理目录,过滤掉隐藏目录和 node_modules
28
+ if (stat.isDirectory() && !dir.startsWith('.') && dir !== 'node_modules') {
29
+ cmpTypes.push(dir);
30
+ }
31
+ });
32
+
33
+ return cmpTypes;
34
+ } catch (error) {
35
+ console.error('获取组件类型失败(getCmpTypeByDir):', error);
36
+ // 退出进程
37
+ process.exit(1);
38
+ }
39
+ };
40
+
41
+ module.exports = getCmpTypeByDir;
@@ -0,0 +1,11 @@
1
+ const getCmpTypeByDir = require('./getCmpTypeByDir');
2
+ /**
3
+ * 判断当前组件目录是否已经存在该组件类型
4
+ * @param {*} componentsBaseDir 自定义组件目录
5
+ * @param {*} cmpType 组件类型
6
+ * @returns Boolean
7
+ */
8
+ module.exports = (cmpType) => {
9
+ const cmpTypes = getCmpTypeByDir();
10
+ return cmpTypes.includes(cmpType);
11
+ };;
@@ -2,7 +2,7 @@ const fs = require('fs');
2
2
  const akfun = require('akfun');
3
3
  const { consoleTag } = require('../utils/neoParams'); // 输出标记
4
4
  const { resolveToCurrentRoot } = require('../utils/pathUtils');
5
- const getCmpPreview = require('../cmpUtils/getCmpPreview');
5
+ const getCmpPreviewCode = require('../cmpUtils/getCmpPreviewCode'); // 获取自定义组件预览代码
6
6
 
7
7
  /**
8
8
  * 用于预览指定自定义组件的脚本
@@ -35,7 +35,7 @@ module.exports = (config, cmpName, defaultComponentsDir = './src/components') =>
35
35
  fs.mkdirSync(cmpTempDir);
36
36
  }
37
37
 
38
- const cmpPreviewContent = getCmpPreview(cmpsDir, cmpName);
38
+ const cmpPreviewContent = getCmpPreviewCode(cmpsDir, cmpName);
39
39
  fs.writeFileSync(`${cmpTempDir}/preview.jsx`, cmpPreviewContent);
40
40
 
41
41
  // 将临时预览文件添加到预览配置中
@@ -0,0 +1,232 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const AdmZip = require('adm-zip');
4
+ const _ = require('lodash');
5
+ const { catchCurPackageJson } = require('../utils/pathUtils');
6
+ const getConfigObj = require('../utils/getConfigObj');
7
+ const ora = require('ora');
8
+ const NeoService = require('../neo/neoService');
9
+ const { consoleTag } = require('../utils/neoParams');
10
+
11
+ // 获取当前项目的package文件
12
+ const currentPackageJsonDir = catchCurPackageJson();
13
+ const currentPackageJson = getConfigObj(currentPackageJsonDir);
14
+
15
+ /**
16
+ * 将构建产物打包成 zip 文件
17
+ * @param {string} assetsRoot 构建产物的目录
18
+ * @returns {Promise<string>} zip 文件路径
19
+ */
20
+ const createZipPackage = async (assetsRoot) => {
21
+ if (!fs.existsSync(assetsRoot)) {
22
+ throw new Error(`assetsRoot 不存在: ${assetsRoot}`);
23
+ }
24
+
25
+ const files = fs.readdirSync(assetsRoot);
26
+ const zip = new AdmZip();
27
+
28
+ files.forEach((file) => {
29
+ const filePath = path.join(assetsRoot, file);
30
+ const fileStat = fs.statSync(filePath);
31
+ if (fileStat.isFile()) {
32
+ // 只添加 .js 文件
33
+ if (file.endsWith('.js')) {
34
+ zip.addLocalFile(filePath);
35
+ }
36
+ }
37
+ });
38
+
39
+ const zipPath = path.join(assetsRoot, `${currentPackageJson.name}.zip`);
40
+ zip.writeZip(zipPath);
41
+ console.info(`已创建 zip 文件: ${zipPath}`);
42
+
43
+ return zipPath;
44
+ };
45
+
46
+ /**
47
+ * 获取技术栈标识
48
+ * 目的:兼容用户非标准写法
49
+ * 0: React, 1: vue2, 2: jQuery, 3: vue3
50
+ */
51
+ function getFramework(_framework) {
52
+ let defaultFramework = '0'; // 默认 React 技术栈
53
+ if (!_framework) {
54
+ return defaultFramework;
55
+ }
56
+ let curFramework = _framework.toLowerCase().trim();
57
+ switch (curFramework) {
58
+ case 'jquery':
59
+ case 'jq':
60
+ curFramework = '2';
61
+ break;
62
+ case 'vue2':
63
+ case 'vue 2':
64
+ case 'vue2.0':
65
+ case 'vue 2.0':
66
+ curFramework = '1';
67
+ break;
68
+ case 'vue':
69
+ case 'vue3':
70
+ case 'vue 3':
71
+ case 'vue3.0':
72
+ case 'vue 3.0':
73
+ curFramework = '3';
74
+ console.error(`${consoleTag} 暂不支持 vue3.0 技术栈。`);
75
+ break;
76
+ default:
77
+ curFramework = '0';
78
+ }
79
+ return curFramework;
80
+ }
81
+
82
+ /**
83
+ * 构建组件数据映射
84
+ * @param {string} assetsRoot 构建产物的目录
85
+ * @param {object} cmpInfo 自定义组件信息
86
+ * @returns {Promise<object|null>} 自定义组件数据(含自定义组件模型信息)
87
+ */
88
+ const buildComponentData = async (assetsRoot, cmpInfo) => {
89
+ if (!cmpInfo || !cmpInfo.cmpType) {
90
+ console.error('自定义组件信息或组件名称不能为空');
91
+ return null;
92
+ }
93
+
94
+ const { cmpType } = cmpInfo;
95
+
96
+ if (!assetsRoot || !fs.existsSync(assetsRoot)) {
97
+ console.error(`未找到自定义组件目录: ${assetsRoot}`);
98
+ return null;
99
+ }
100
+ const widgetName = _.camelCase(cmpType);
101
+ const modelFile = path.join(assetsRoot, `${widgetName}Model.js`);
102
+
103
+ // 为 Node.js 环境设置全局 window 对象(模型文件可能需要)
104
+ // 使用 globalThis 以确保在 Node.js 和浏览器环境中都能工作
105
+ const originalWindow = globalThis.window;
106
+ if (!globalThis.window) {
107
+ globalThis.window = {
108
+ console: console,
109
+ neoRequire: () => {},
110
+ postMessage: () => {},
111
+ // 可以添加其他常用的 window 属性
112
+ };
113
+ }
114
+
115
+ try {
116
+ // 加载自定义组件模型资源文件
117
+ if (fs.existsSync(modelFile)) {
118
+ let modelModule = require(modelFile);
119
+ // 获取导出的模型类(可能是 default 导出或命名导出)
120
+ CatchCustomCmpModelClass = modelModule.default || modelModule;
121
+ }
122
+ else {
123
+ console.error(`未找到自定义组件模型文件,请检查以下路径是否存在:`, modelFile);
124
+ return null;
125
+ }
126
+ if (!window.NEOEditorCustomModels) {
127
+ console.error(`模型文件未导出有效模型方法(CatchCustomCmpModelClass),模型文件地址: ${modelFile} `);
128
+ return null;
129
+ }
130
+
131
+ const ModelClass = window.NEOEditorCustomModels[cmpType];
132
+ if (!ModelClass) {
133
+ console.error(`未找到自定义组件模型类(${cmpType}),模型文件地址: ${modelFile} `);
134
+ return null;
135
+ }
136
+ // 实例化模型类
137
+ const modelInstance = new ModelClass();
138
+
139
+ if (!modelInstance) {
140
+ console.error(`未找到自定义组件模型信息(${cmpType}),模型文件地址: ${modelFile} `);
141
+ return null;
142
+ }
143
+
144
+ // 构建组件数据,合并模型实例的信息
145
+ const curCmpInfo = {
146
+ ...cmpInfo,
147
+ plugin: cmpInfo.modelAsset,
148
+ version: currentPackageJson.version || '1.0.0',
149
+ framework: currentPackageJson.framework ? getFramework(currentPackageJson.framework) : '0', // 0: React, 1: vue2, 2: jQuery, 3: vue3
150
+ // 从模型实例中提取并设置组件信息
151
+ label: modelInstance.label || cmpType,
152
+ description: modelInstance.description || '',
153
+ componentCategory: (modelInstance.tags || []).join(','),
154
+ icon: modelInstance.iconSrc,
155
+ defaultProps: JSON.stringify(modelInstance.defaultComProps || {}),
156
+ previewProps: JSON.stringify(modelInstance.previewComProps || {}),
157
+ propsSchema: JSON.stringify(modelInstance.propsSchema || []),
158
+ events: modelInstance.events || [],
159
+ actions: modelInstance.actions || [],
160
+ // 如果模型实例中有其他属性,也可以添加
161
+ exposedToDesigner: modelInstance.exposedToDesigner !== undefined ? modelInstance.exposedToDesigner : true,
162
+ namespace: modelInstance.namespace || 'neo-cmp-cli',
163
+ enableDuplicate: modelInstance.enableDuplicate !== undefined ? modelInstance.enableDuplicate : true
164
+ };
165
+
166
+ console.log(`自定义组件模型信息(${cmpType}):`, curCmpInfo);
167
+ return curCmpInfo;
168
+ } catch (error) {
169
+ console.error(`自定义组件模型文件解析失败 (${modelFile || '未知路径'}):`, error.message);
170
+ console.error(error.stack);
171
+ return null;
172
+ } finally {
173
+ // 恢复原始的 window 对象(如果之前存在)
174
+ if (originalWindow === undefined) {
175
+ delete globalThis.window;
176
+ } else {
177
+ globalThis.window = originalWindow;
178
+ }
179
+ }
180
+ };
181
+
182
+ /**
183
+ * 发布组件到 NeoCRM
184
+ * @param {object} config 配置信息
185
+ * @param {string} assetsRoot 构建产物的目录
186
+ */
187
+ const pushCmp = async (config, cmpType) => {
188
+ const {
189
+ authConfig: credentials
190
+ } = config;
191
+
192
+ if (!credentials) {
193
+ console.error('未找到 NeoCRM 平台授权配置(neo.config.js / pushCmp / authConfig)。');
194
+ return;
195
+ }
196
+
197
+ const spinner = ora('正在发布组件...').start();
198
+
199
+ try {
200
+ // 步骤 1: 初始化 NeoService
201
+ spinner.text = '发布自定义组件:初始化 NeoService...';
202
+ const neoService = new NeoService(config);
203
+
204
+ // 步骤 2: 上传构建后资源文件
205
+ spinner.text = '发布自定义组件:上传自定义组件构建产物到 OSS...';
206
+ const cmpInfo = await neoService.publish2oss(cmpType);
207
+
208
+ /*
209
+ // 步骤 3: 打包文件(打包单个自定义组件源码)
210
+ spinner.text = '发布自定义组件:打包文件(打包单个自定义组件源码)...';
211
+ const zipPath = await createZipPackage(assetsRoot);
212
+ */
213
+
214
+ // 步骤 4: 构建组件数据
215
+ spinner.text = '发布自定义组件:构建组件数据...';
216
+ const componentInfo = await buildComponentData(config.assetsRoot, cmpInfo);
217
+ if (!componentInfo) {
218
+ throw new Error(`构建组件数据失败,未获取到自定义组件模型信息。(${cmpType})`);
219
+ }
220
+
221
+ // 步骤 5: 保存组件信息
222
+ spinner.text = '发布自定义组件:保存组件信息...';
223
+ await neoService.updateCustomComponent(componentInfo);
224
+
225
+ spinner.succeed('自定义组件发布成功!\n', componentInfo);
226
+ } catch (error) {
227
+ spinner.fail(`自定义组件发布失败: ${error.message}`);
228
+ throw error;
229
+ }
230
+ };
231
+
232
+ module.exports = pushCmp;
@@ -34,8 +34,8 @@ const defaultNEOConfig = {
34
34
  template: resolveByDirname('../initData/defaultTemplate.html'), // 默认使用neo-widget提供的页面模板(会启动页面设计器)
35
35
  sassResources: [],
36
36
  babelPlugins: [
37
- ['import', { libraryName: 'antd', style: 'css' }], // 配置 antd 的样式按需引入
38
- ],
37
+ ['import', { libraryName: 'antd', style: 'css' }] // 配置 antd 的样式按需引入
38
+ ]
39
39
  },
40
40
  envParams: {
41
41
  // 项目系统环境变量
@@ -132,6 +132,18 @@ const defaultNEOConfig = {
132
132
  bucket: 'neo-widgets' // 存储桶名称
133
133
  },
134
134
  assetsRoot: resolve('dist') // 上传指定目录下的脚本文件
135
+ },
136
+ pushCmp: {
137
+ output: {
138
+ filename: '[name].js',
139
+ library: {
140
+ type: 'var', // webpack 5 中生成 IIFE 格式的 type 配置
141
+ export: 'default'
142
+ },
143
+ globalObject: 'this' // 定义全局变量,兼容node和浏览器运行,避免出现"window is not defined"的情况
144
+ },
145
+ cssExtract: false, // 不额外提取css文件
146
+ assetsRoot: resolve('dist') // 上传指定目录下的脚本文件
135
147
  }
136
148
  };
137
149