neo-cmp-cli 1.5.1 → 1.5.3

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 (69) hide show
  1. package/README.md +52 -15
  2. package/package.json +1 -1
  3. package/src/cmpUtils/createCmpByTemplate.js +58 -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/{publishCmp.js → pushCmp.js} +54 -42
  12. package/src/config/default.config.js +14 -2
  13. package/src/module/index.js +144 -21
  14. package/src/module/main.js +17 -13
  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 +174 -72
  20. package/src/neo/wrapperContent.js +2 -1
  21. package/src/oss/publish2oss.js +1 -1
  22. package/src/plugins/AddNeoRequirePlugin.js +5 -2
  23. package/src/projectUtils/createCmpProjectByTemplate.js +55 -0
  24. package/src/{cmpUtils → projectUtils}/getEntriesWithAutoRegister.js +4 -4
  25. package/src/projectUtils/hasNeoProject.js +16 -0
  26. package/src/template/antd-custom-cmp-template/README.md +2 -2
  27. package/src/template/antd-custom-cmp-template/neo.config.js +22 -14
  28. package/src/template/antd-custom-cmp-template/package.json +3 -3
  29. package/src/template/develop/neo-custom-cmp-template/neo.config.js +1 -1
  30. package/src/template/echarts-custom-cmp-template/README.md +2 -2
  31. package/src/template/echarts-custom-cmp-template/neo.config.js +19 -13
  32. package/src/template/echarts-custom-cmp-template/package.json +3 -3
  33. package/src/template/empty-cmp/index.tsx +51 -0
  34. package/src/template/empty-cmp/model.ts +77 -0
  35. package/src/template/empty-cmp/style.scss +72 -0
  36. package/src/template/empty-custom-cmp-template/.prettierrc.js +12 -0
  37. package/src/template/empty-custom-cmp-template/README.md +45 -0
  38. package/src/template/empty-custom-cmp-template/commitlint.config.js +59 -0
  39. package/src/template/empty-custom-cmp-template/neo.config.js +126 -0
  40. package/src/template/empty-custom-cmp-template/package.json +57 -0
  41. package/src/template/empty-custom-cmp-template/public/css/base.css +283 -0
  42. package/src/template/empty-custom-cmp-template/public/scripts/app/bluebird.js +6679 -0
  43. package/src/template/empty-custom-cmp-template/public/template.html +13 -0
  44. package/src/template/empty-custom-cmp-template/src/assets/css/common.scss +127 -0
  45. package/src/template/empty-custom-cmp-template/src/assets/css/mixin.scss +47 -0
  46. package/src/template/empty-custom-cmp-template/src/assets/img/NeoCRM.jpg +0 -0
  47. package/src/template/empty-custom-cmp-template/src/assets/img/custom-widget.svg +1 -0
  48. package/src/template/empty-custom-cmp-template/src/assets/img/favicon.png +0 -0
  49. package/src/template/empty-custom-cmp-template/src/assets/img/map.svg +1 -0
  50. package/src/template/empty-custom-cmp-template/src/components/README.md +3 -0
  51. package/src/template/empty-custom-cmp-template/tsconfig.json +68 -0
  52. package/src/template/neo-custom-cmp-template/README.md +2 -2
  53. package/src/template/neo-custom-cmp-template/neo.config.js +6 -26
  54. package/src/template/neo-custom-cmp-template/package.json +3 -4
  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 +3 -3
  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 +3 -3
  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 +3 -3
  64. package/src/utils/autoEntryRootDir.js +75 -0
  65. package/src/utils/replaceInFilesByMap.js +54 -0
  66. package/test/demo.js +2 -2
  67. package/src/template/neo-custom-cmp-template/auth.config.js +0 -12
  68. /package/src/{cmpUtils → projectUtils}/getEntries.js +0 -0
  69. /package/src/{cmpUtils → projectUtils}/updatePublishLog.js +0 -0
@@ -3,14 +3,14 @@ const FormData = require('form-data');
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const _ = require('lodash');
6
- const updatePublishLog = require('../cmpUtils/updatePublishLog');
6
+ const updatePublishLog = require('../projectUtils/updatePublishLog');
7
7
 
8
8
  // NeoCRM 平台默认 API 配置
9
9
  const NeoCrmAPI = {
10
10
  neoBaseURL: 'https://crm.xiaoshouyi.com', // 平台根地址
11
- tokenAPI: 'https://login.crm.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
11
+ tokenAPI: 'https://login.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
12
12
  uploadAPI: '/rest/metadata/v3.0/ui/customComponents/actions/upload', // 文件上传接口地址
13
- saveAPI: '/rest/metadata/v3.0/ui/customComponents/actions/saveComponent' // 组件信息保存接口地址
13
+ saveAPI: '/rest/metadata/v3.0/ui/customComponents/actions/saveOrUpdateComponent' // 创建或者保存接口地址
14
14
  };
15
15
 
16
16
  /**
@@ -23,32 +23,32 @@ class NeoService {
23
23
  * @param {object} config 配置信息
24
24
  * @param {string} config.neoBaseURL Neo 平台根地址
25
25
  * @param {string} config.tokenAPI Token 获取接口地址
26
- * @param {object} config.authorization 授权信息
27
- * @param {string} config.authorization.client_id 客户端 ID
28
- * @param {string} config.authorization.client_secret 客户端密钥
29
- * @param {string} config.authorization.username 用户名
30
- * @param {string} config.authorization.password 密码
26
+ * @param {object} config.authConfig 授权信息
27
+ * @param {string} config.authConfig.client_id 客户端 ID
28
+ * @param {string} config.authConfig.client_secret 客户端密钥
29
+ * @param {string} config.authConfig.username 用户名
30
+ * @param {string} config.authConfig.password 密码
31
31
  */
32
32
  constructor(config = {}) {
33
- const { assetsRoot, neoBaseURL, tokenAPI, authorization } = config;
34
- if (!authorization) {
35
- throw new Error('authorization 不能为空');
33
+ const { assetsRoot, neoBaseURL, tokenAPI, authConfig } = config;
34
+ if (!authConfig) {
35
+ throw new Error('authConfig 不能为空');
36
36
  }
37
37
  if (
38
- !authorization.client_id ||
39
- !authorization.client_secret ||
40
- !authorization.username ||
41
- !authorization.password
38
+ !authConfig.client_id ||
39
+ !authConfig.client_secret ||
40
+ !authConfig.username ||
41
+ !authConfig.password
42
42
  ) {
43
43
  throw new Error(
44
- 'authorization 配置不完整,需要包含 client_id、client_secret、username、password'
44
+ 'authConfig 配置不完整,需要包含 client_id、client_secret、username、password'
45
45
  );
46
46
  }
47
47
 
48
48
  this.assetsRoot = assetsRoot;
49
49
  this.neoBaseURL = neoBaseURL || NeoCrmAPI.neoBaseURL;
50
50
  this.tokenAPI = tokenAPI || NeoCrmAPI.tokenAPI;
51
- this.authorization = authorization;
51
+ this.authConfig = authConfig;
52
52
 
53
53
  // Token 缓存
54
54
  this.tokenCache = {
@@ -101,29 +101,30 @@ class NeoService {
101
101
  console.info('使用缓存的 token');
102
102
  return this.tokenCache.token;
103
103
  }
104
+ console.info('获取 token...');
104
105
 
105
- console.info('正在获取 token...');
106
+ // 构建表单数据格式的请求参数
107
+ const formData = new URLSearchParams();
108
+ formData.append('grant_type', 'password');
109
+ formData.append('client_id', this.authConfig.client_id);
110
+ formData.append('client_secret', this.authConfig.client_secret);
111
+ formData.append('username', this.authConfig.username);
112
+ formData.append('password', this.authConfig.password);
113
+
114
+ const tokenUrl = this.buildFullUrl(this.tokenAPI);
106
115
 
107
116
  try {
108
- // 构建请求参数
109
- const params = new URLSearchParams();
110
- params.append('grant_type', 'password');
111
- params.append('client_id', this.authorization.client_id);
112
- params.append('client_secret', this.authorization.client_secret);
113
- params.append('username', this.authorization.username);
114
- params.append('password', this.authorization.password);
115
-
116
- const tokenUrl = this.buildFullUrl(this.tokenAPI);
117
- const response = await axios.post(tokenUrl, params, {
117
+ const response = await axios.post(tokenUrl, formData.toString(), {
118
118
  headers: {
119
119
  'Content-Type': 'application/x-www-form-urlencoded'
120
120
  }
121
121
  });
122
122
 
123
- const { access_token, expires_in } = response.data;
123
+ const { access_token, expires_in } = response.data || {};
124
124
 
125
125
  if (!access_token) {
126
- throw new Error('获取 token 失败:响应中未包含 access_token');
126
+ console.error('\n获取 token 失败:响应中未包含 access_token', response.data);
127
+ process.exit(1);
127
128
  }
128
129
 
129
130
  // 缓存 token(提前 60 秒过期,避免边缘情况)
@@ -132,15 +133,15 @@ class NeoService {
132
133
  token: access_token,
133
134
  expiresAt: Date.now() + (expiresIn - 60) * 1000
134
135
  };
135
-
136
- console.info('token 获取成功');
137
136
  return access_token;
138
137
  } catch (error) {
139
- console.error('获取 token 失败:', error.message);
138
+ console.error('\n获取 token 失败:', error.message);
139
+ console.error('\ntoken 授权地址:', tokenUrl);
140
+ console.error('\ntoken 请求参数:', formData);
140
141
  if (error.response) {
141
142
  console.error('响应数据:', error.response.data);
142
143
  }
143
- throw error;
144
+ process.exit(1);
144
145
  }
145
146
  }
146
147
 
@@ -163,7 +164,9 @@ class NeoService {
163
164
  * @returns {Promise<string>} 有效的 token
164
165
  */
165
166
  async ensureValidToken() {
166
- if (this.isTokenExpired()) {
167
+ if (!this.tokenCache.token) {
168
+ return await this.getToken();
169
+ } else if (this.isTokenExpired()) {
167
170
  console.info('token 已过期,正在刷新...');
168
171
  return await this.refreshToken();
169
172
  }
@@ -174,59 +177,155 @@ class NeoService {
174
177
  * 上传文件到 Neo 平台
175
178
  * @param {string} filePath 文件路径
176
179
  * @param {object} options 可选配置
177
- * @param {string} options.fieldName 表单字段名,默认为 'file'
180
+ * @param {string} options.fieldName 表单字段名,默认为 'customComponentCode'
181
+ * @param {number} options.maxSize 最大文件大小(字节),默认 50MB
182
+ * @param {number} options.timeout 超时时间(毫秒),默认 60000
178
183
  * @returns {Promise<string>} CDN 地址或文件 URL
179
184
  */
180
185
  async uploadFile(filePath, options = {}) {
181
186
  // 确保 token 有效
182
187
  const token = await this.ensureValidToken();
183
188
 
189
+ // 验证文件路径
190
+ if (!filePath || typeof filePath !== 'string') {
191
+ throw new Error(`文件路径无效: ${filePath}`);
192
+ }
193
+
194
+ // 检查文件是否存在
184
195
  if (!fs.existsSync(filePath)) {
185
196
  throw new Error(`文件不存在: ${filePath}`);
186
197
  }
187
198
 
188
- console.info('正在上传文件...');
199
+ // 检查文件状态
200
+ const fileStat = fs.statSync(filePath);
201
+ if (!fileStat.isFile()) {
202
+ throw new Error(`路径不是文件: ${filePath}`);
203
+ }
204
+
205
+ // 检查文件大小
206
+ const maxSize = options.maxSize || 50 * 1024 * 1024; // 默认 50MB
207
+ if (fileStat.size > maxSize) {
208
+ const sizeMB = (fileStat.size / 1024 / 1024).toFixed(2);
209
+ const maxSizeMB = (maxSize / 1024 / 1024).toFixed(2);
210
+ throw new Error(`文件大小超过限制: ${sizeMB}MB > ${maxSizeMB}MB`);
211
+ }
212
+
213
+ if (fileStat.size === 0) {
214
+ throw new Error(`文件为空: ${filePath}`);
215
+ }
216
+
217
+ const fileName = path.basename(filePath);
218
+ const fileSizeKB = (fileStat.size / 1024).toFixed(2);
219
+ console.info(`正在上传文件: ${fileName} (${fileSizeKB}KB)...`);
189
220
 
190
221
  try {
222
+ // 创建 FormData
191
223
  const formData = new FormData();
192
224
  const fieldName = options.fieldName || 'customComponentCode';
193
- formData.append(fieldName, fs.createReadStream(filePath));
225
+
226
+ // 使用文件流而不是读取整个文件到内存(对大文件更友好)
227
+ const fileContent = fs.createReadStream(filePath);
228
+
229
+ // 追加文件到 FormData,第三个参数指定文件名
230
+ formData.append(fieldName, fileContent, fileName);
194
231
 
232
+ // 构建完整的上传 API 地址
195
233
  const fullUploadAPI = this.uploadAPI();
196
- const response = await axios.post(fullUploadAPI, formData, {
234
+
235
+ // 配置请求选项
236
+ const timeout = options.timeout || 60000; // 默认 60 秒
237
+ const requestConfig = {
197
238
  headers: {
198
239
  Authorization: `Bearer ${token}`,
240
+ 'xsy-inner-source': 'bff',
241
+ // 无需手动设置 Content-Type,formData.getHeaders() 会自动设置正确的 multipart/form-data 和 boundary
199
242
  ...formData.getHeaders()
200
- }
201
- });
243
+ },
244
+ timeout,
245
+ // 确保 axios 正确处理大文件和流
246
+ maxContentLength: Infinity, // 不限制响应内容长度
247
+ maxBodyLength: Infinity // 不限制请求体长度(适用于文件上传)
248
+ };
202
249
 
203
- // 处理不同的响应格式
250
+ // 发送上传请求
251
+ const response = await axios.post(fullUploadAPI, formData, requestConfig);
252
+
253
+ // 处理响应数据
204
254
  let resultData;
205
- if (typeof response.data === 'string') {
206
- resultData = response.data;
207
- } else if (response.data && response.data.data) {
208
- resultData = response.data.data;
209
- } else if (response.data && response.data.code === 200) {
210
- resultData = response.data.data;
211
- } else {
212
- resultData = response.data;
255
+ const responseData = response.data;
256
+
257
+ // 检查响应状态码
258
+ if (response.status !== 200 && response.status !== 201) {
259
+ throw new Error(`上传失败: HTTP ${response.status}`);
213
260
  }
214
261
 
215
- if (response.data && response.data.code && response.data.code !== 200) {
216
- throw new Error(`上传失败: ${response.data.message || '未知错误'}`);
262
+ // 处理不同的响应格式
263
+ if (typeof responseData === 'string') {
264
+ // 如果响应是字符串,直接使用
265
+ resultData = responseData.trim();
266
+ } else if (responseData && typeof responseData === 'object') {
267
+ // 检查是否有错误码
268
+ if (responseData.code !== undefined && responseData.code !== 200 && responseData.code !== 0) {
269
+ const errorMsg = responseData.message || responseData.msg || '未知错误';
270
+ throw new Error(`上传失败: ${errorMsg} (code: ${responseData.code})`);
271
+ }
272
+
273
+ // 提取数据
274
+ if (responseData.data !== undefined) {
275
+ resultData = responseData.data;
276
+ } else if (responseData.url !== undefined) {
277
+ resultData = responseData.url;
278
+ } else if (responseData.fileUrl !== undefined) {
279
+ resultData = responseData.fileUrl;
280
+ } else {
281
+ resultData = responseData;
282
+ }
283
+ } else {
284
+ throw new Error(`响应数据格式不正确: ${typeof responseData}`);
217
285
  }
218
286
 
219
- if (!resultData || (typeof resultData !== 'string' && !resultData.url)) {
220
- throw new Error(`返回的文件地址格式不正确: ${JSON.stringify(resultData)}`);
287
+ // 验证返回的文件地址
288
+ if (!resultData) {
289
+ throw new Error(`返回的文件地址为空`);
221
290
  }
222
291
 
223
- const fileUrl = typeof resultData === 'string' ? resultData : resultData.url;
292
+ // 格式化文件 URL
293
+ let fileUrl;
294
+ if (typeof resultData === 'string') {
295
+ fileUrl = resultData;
296
+ } else if (resultData && typeof resultData === 'object' && resultData.url) {
297
+ fileUrl = resultData.url;
298
+ }
299
+ console.info(`\n文件上传成功: ${fileName} -> ${fileUrl}`);
224
300
  return fileUrl;
225
301
  } catch (error) {
226
- console.error('上传文件失败:', error.message);
302
+ console.error(`\n上传文件失败: ${error.message},`);
303
+ console.error(`文件路径: ${filePath}。\n`);
304
+
305
+ // 输出详细的错误信息
227
306
  if (error.response) {
228
- console.error('响应数据:', error.response.data);
307
+ const status = error.response.status;
308
+ const statusText = error.response.statusText;
309
+ const responseData = error.response.data;
310
+ const requestUrl = error.config?.url || this.uploadAPI();
311
+
312
+ console.error(`\n========== 上传请求详情 ==========`);
313
+ console.error(`请求 URL: ${requestUrl}`);
314
+ console.error(`HTTP 状态码: ${status} ${statusText}`);
315
+ console.error(`响应数据:`, responseData);
316
+ console.error(`==================================\n`);
317
+
318
+ if (status === 404) {
319
+ throw new Error(
320
+ `上传 API 不存在 (404): ${requestUrl}\n` +
321
+ `请检查 neo.config.js 中的 neoBaseURL 配置是否正确,或者 API 路径是否存在。\n` +
322
+ `当前配置的 API 路径: ${NeoCrmAPI.uploadAPI}`
323
+ );
324
+ }
325
+ } else if (error.request) {
326
+ console.error('请求已发送但未收到响应,请检查网络连接或代理配置。');
229
327
  }
328
+
230
329
  throw error;
231
330
  }
232
331
  }
@@ -238,7 +337,7 @@ class NeoService {
238
337
  * @param {array} fileExtensions 需要上传的文件类型,默认 ['.js', '.css']
239
338
  */
240
339
  async publish2oss(cmpType, fileExtensions = ['.js', '.css']) {
241
- if (!fs.existsSync(cmpType)) {
340
+ if (!cmpType) {
242
341
  console.error(`自定义组件名称不能为空: ${cmpType}`);
243
342
  return;
244
343
  }
@@ -262,7 +361,7 @@ class NeoService {
262
361
  // const fileExt = path.extname(file);
263
362
  const fileInfo = path.parse(filePath);
264
363
  if (fileStat.isFile() && fileExtensions.includes(fileInfo.ext)) {
265
- let widgetName = _.kebabCase(cmpType);
364
+ let widgetName = _.camelCase(cmpType);
266
365
 
267
366
  if (file.indexOf(widgetName) < 0) {
268
367
  return;
@@ -271,7 +370,7 @@ class NeoService {
271
370
  try {
272
371
  // 上传文件
273
372
  const fileUrl = await this.uploadFile(filePath);
274
-
373
+
275
374
  if (file.indexOf('Model') > -1) {
276
375
  curCmpInfo.modelAsset = fileUrl;
277
376
  } else if (file.endsWith('.css')) {
@@ -279,15 +378,15 @@ class NeoService {
279
378
  } else {
280
379
  curCmpInfo.asset = fileUrl;
281
380
  }
282
-
283
381
  } catch (error) {
284
- console.error(`文件上传失败(${file}):\n`, error);
382
+ console.error(`文件上传失败(${file}):\n`);
383
+ process.exit(1);
285
384
  }
286
385
  }
287
386
  });
288
387
 
289
388
  await Promise.all(uploadPromises);
290
-
389
+
291
390
  if (curCmpInfo && curCmpInfo.cmpType) {
292
391
  console.info('上传至 OSS 的文件信息:\n', curCmpInfo);
293
392
  // 更新发布日志
@@ -298,7 +397,6 @@ class NeoService {
298
397
 
299
398
  /**
300
399
  * 更新自定义组件
301
- * @param {string} updateAPI 更新接口地址(相对或绝对路径)
302
400
  * @param {object} componentData 组件数据
303
401
  * @returns {Promise<object>} 更新结果
304
402
  */
@@ -313,26 +411,29 @@ class NeoService {
313
411
  console.info('正在更新自定义组件...');
314
412
 
315
413
  try {
316
- const fullUpdateAPI = this.updateAPI();
414
+ const fullUpdateAPI = this.saveAPI();
317
415
  const response = await axios.post(fullUpdateAPI, componentData, {
318
416
  headers: {
319
417
  Authorization: `Bearer ${token}`,
418
+ 'xsy-inner-source': 'bff',
320
419
  'Content-Type': 'application/json'
321
420
  }
322
421
  });
422
+ const {code, message} = response.data || {};
323
423
 
324
- if (response.data && response.data.code && response.data.code !== 200) {
424
+ if (code && code !== 200) {
325
425
  throw new Error(`更新组件失败: ${response.data.message || '未知错误'}`);
326
426
  }
427
+
327
428
 
328
- console.info('组件更新成功:', response.data);
329
- return response.data;
429
+ console.info(message ? `组件更新成功: ${message}。` : '组件更新成功。');
330
430
  } catch (error) {
331
- console.error('更新组件失败:', error.message);
332
- if (error.response) {
333
- console.error('响应数据:', error.response.data);
431
+ if (error.message) {
432
+ console.error('更新组件失败:', error.message);
433
+ } else {
434
+ console.error('响应数据:', error);
334
435
  }
335
- throw error;
436
+ process.exit(1);
336
437
  }
337
438
  }
338
439
 
@@ -361,6 +462,7 @@ class NeoService {
361
462
  params,
362
463
  headers: {
363
464
  Authorization: `Bearer ${token}`,
465
+ 'xsy-inner-source': 'bff',
364
466
  ...headers
365
467
  }
366
468
  });
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 注入 neoRequire 函数
3
3
  * 备注:用于实现和 Neo 平台共享依赖
4
+ * 特别说明:这个文件只是用于说明 AddNeoRequirePlugin 中会插入的代码内容,实际内容内置在 AddNeoRequirePlugin 方法中。
4
5
  */
5
6
  (function(NeoCustomCmpFileFactory) {
6
7
  if (!window.neoRequire) {
@@ -12,4 +13,4 @@
12
13
  * 这里放自定义组件相关内容代码
13
14
  * 备注: 自定义组件代码中的 require 函数 已被替换成 neoRequire 函数(require === neoRequire)。
14
15
  */
15
- });
16
+ });
@@ -4,7 +4,7 @@ const path = require('path');
4
4
  const _ = require('lodash');
5
5
  const { catchCurPackageJson } = require('../utils/pathUtils');
6
6
  const getConfigObj = require('../utils/getConfigObj');
7
- const updatePublishLog = require('../cmpUtils/updatePublishLog');
7
+ const updatePublishLog = require('../projectUtils/updatePublishLog');
8
8
 
9
9
  // 获取当前项目的package文件
10
10
  const currentPackageJsonDir = catchCurPackageJson();
@@ -1,5 +1,6 @@
1
1
  const { ConcatSource } = require('webpack-sources');
2
-
2
+ const fs = require('fs');
3
+ const path = require('path');
3
4
  /**
4
5
  * 注入 neoRequire 函数
5
6
  * 备注:用于实现和 Neo 平台共享依赖
@@ -163,7 +164,9 @@ class AddNeoRequirePlugin {
163
164
  })(function(require) {
164
165
  `;
165
166
 
166
- const Footer = `})`;
167
+ // const NeoUMDContent = fs.readFileSync(path.join(__dirname, '../neo/NeoUMDContent.js'), 'utf8');
168
+ // const Footer = `}); \n${NeoUMDContent}`;
169
+ const Footer = `});`;
167
170
 
168
171
  // 创建新的资源
169
172
  const newSource = new ConcatSource(Header, content, Footer);
@@ -0,0 +1,55 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const { consoleTag } = require('../utils/neoParams'); // 输出标记
4
+ const { replaceInPackage } = require('../utils/replaceInPackage');
5
+ const { resetPackageVersion } = require('../utils/resetPackageVersion');
6
+ const autoEntryRootDir = require('../utils/autoEntryRootDir');
7
+ const hasNeoProject = require('./hasNeoProject');
8
+
9
+ const cmpTemplateList = {
10
+ 'react-ts': {
11
+ projectName: 'empty-custom-cmp-template',
12
+ dir: path.resolve(__dirname, '../template/empty-custom-cmp-template')
13
+ }
14
+ };
15
+
16
+ /**
17
+ * 通过拷贝模板创建自定义组件
18
+ * @param {*} projectName 自定义组件项目名称
19
+ * @param {*} type 自定义组件类型
20
+ */
21
+ module.exports = function (projectName, type = 'react-ts') {
22
+ const finalProjectPath = path.resolve(process.cwd(), projectName);
23
+
24
+ if (hasNeoProject()) {
25
+ console.error(`${consoleTag}创建自定义组件项目失败,当前目录(${process.cwd()})已经是一个 自定义组件项目,请勿重复创建。`);
26
+ process.exit(1);
27
+ }
28
+
29
+ if (fs.existsSync(finalProjectPath)) {
30
+ console.error(`${consoleTag}创建自定义组件项目失败,当前已存在(${projectName})项目,请勿创建重名项目。`);
31
+ process.exit(1);
32
+ }
33
+
34
+ const curCmpTemplate = cmpTemplateList[type];
35
+ if (!curCmpTemplate) {
36
+ console.error(`${consoleTag}创建自定义组件项目失败,当前不支持${type}类型。`);
37
+ process.exit(1);
38
+ }
39
+
40
+ const currentTemplateDir = curCmpTemplate.dir;
41
+ const finalProjectName = projectName || 'neoCustomCmp';
42
+
43
+ fs.copy(currentTemplateDir, finalProjectPath)
44
+ .then(() => {
45
+ replaceInPackage(finalProjectPath, curCmpTemplate.projectName, finalProjectName);
46
+ replaceInPackage(finalProjectPath, 'wibetter', 'xxx');
47
+ replaceInPackage(finalProjectPath, 'neo自定义组件模板', 'neo自定义组件');
48
+ resetPackageVersion(finalProjectPath);
49
+
50
+ console.log(`${consoleTag}已创建自定义组件项目(${finalProjectName})!`);
51
+ // 自动切换到项目根目录
52
+ autoEntryRootDir(finalProjectPath);
53
+ })
54
+ .catch((err) => console.error(`${consoleTag}自定义组件模板下载失败:`, err));
55
+ };
@@ -2,8 +2,8 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const _ = require('lodash');
4
4
  const { resolveToCurrentRoot } = require('../utils/pathUtils');
5
- const getCmpRegister = require('./getCmpRegister');
6
- const getCmpModelRegister = require('./getCmpModelRegister');
5
+ const getCmpRegisterCode = require('../cmpUtils/getCmpRegisterCode');
6
+ const getCmpModelRegisterCode = require('../cmpUtils/getCmpModelRegisterCode');
7
7
  /**
8
8
  * 从指定目录获取组件入口文件,并自动创建对应的注册文件
9
9
  * @param {*} defaultComponentsDir 默认组件目录
@@ -68,7 +68,7 @@ module.exports = (defaultComponentsDir = './src/components', cmpType) => {
68
68
  .forEach((filePath) => {
69
69
  if (filePath.match(/index\.[tj]sx?$/)) {
70
70
  // 自动创建对应的注册文件
71
- const registerContent = getCmpRegister(componentsBaseDir, curCmpName);
71
+ const registerContent = getCmpRegisterCode(componentsBaseDir, curCmpName);
72
72
  const registerDir = `${cmpTempDir}/register.js`;
73
73
 
74
74
  // 写入注册文件
@@ -78,7 +78,7 @@ module.exports = (defaultComponentsDir = './src/components', cmpType) => {
78
78
  linkDebugEntries.index.push(registerDir);
79
79
  } else if (filePath.match(/model\.[tj]sx?$/)) {
80
80
  // 自动创建对应的模型注册文件
81
- const modelRegisterContent = getCmpModelRegister(componentsBaseDir, curCmpName);
81
+ const modelRegisterContent = getCmpModelRegisterCode(componentsBaseDir, curCmpName);
82
82
  const modelRegisterDir = `${cmpTempDir}/model.js`;
83
83
 
84
84
  // 写入模型注册文件
@@ -0,0 +1,16 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * 判断当前是否存在 neo 项目
6
+ * 备注:neo.config.js 和 package.json 必须同时存在才算作 neo 项目
7
+ */
8
+ module.exports = function () {
9
+ const neoConfigPath = path.resolve(process.cwd(), 'neo.config.js');
10
+ const packagePath = path.resolve(process.cwd(), 'package.json');
11
+
12
+ if (fs.existsSync(neoConfigPath) && fs.existsSync(packagePath)) {
13
+ return true;
14
+ }
15
+ return false;
16
+ };
@@ -37,10 +37,10 @@ $ npm run preview
37
37
  $ npm run linkDebug
38
38
  ```
39
39
 
40
- 4. **发布到对象存储服务中**
40
+ 4. **发布到 NeoCRM 平台**
41
41
  > 需要确保 package.json 中的 name 值唯一,version 值不重复。
42
42
  ```bash
43
- $ npm run publish2oss
43
+ $ npm run pushCmp
44
44
  ```
45
45
 
46
46
  ### 配置项说明(neo-cmp-cli)
@@ -41,10 +41,12 @@ module.exports = {
41
41
  // 用于添加 Neo 共享依赖模块和剔除模块
42
42
  /*
43
43
  neoCommonModule: {
44
- remotes: ['chart-widget'], // 远程自定义组件,表示当前自定义组件会用到的远程组件
45
- neoExternals: ['chart-widget'], // 自定义组件中需要剔除的模块,仅支持数组写法,需要和 remotes 配合使用
46
44
  // neoExports: ['xxModule'], // 自定义组件 共享出来的模块,支持数组和对象形式
47
- // neoExports: { 'xxModule': path.resolve('./src/components/xxModule') }, // 对象写法
45
+ neoExports: { // 对象写法
46
+ 'xx-module': path.resolve('./src/components/xx-module'), // 导出 xx组件 或 xx模块
47
+ },
48
+ // remoteDeps: ['xxCmpType'], // 远程依赖组件,表示当前自定义组件会用到的依赖组件,需要和 neoExternals 配合使用
49
+ // neoExternals: ['xxModule'], // 自定义组件中需要剔除的模块(仅支持数组写法)
48
50
  },
49
51
  */
50
52
  preview: {
@@ -95,23 +97,29 @@ module.exports = {
95
97
  }
96
98
  */
97
99
  },
98
- publish2oss: {
99
- // 用于构建并发布至 OSS 的相关配置
100
+ pushCmp: {
101
+ // 用于构建并发布至 NeoCRM 的相关配置
102
+ neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
103
+ tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址(默认:https://login.xiaoshouyi.com/auc/oauth2/token)
104
+ // NeoCRM 授权配置
105
+ authConfig: {
106
+ client_id: 'xx', // 客户端 ID,从创建连接器的客户端信息中获取(Client_Id)
107
+ client_secret: 'xxx', // 客户端秘钥,从创建连接器的客户端信息中获取(Client_Secret)
108
+ username: 'xx', // 用户在销售易系统中的用户名
109
+ /**
110
+ * password 为 用户在销售易系统中的账号密码加上 8 位安全令牌。
111
+ * 例如,用户密码为 123456,安全令牌为 ABCDEFGH,则 password 的值应为 123456ABCDEFGH。
112
+ */
113
+ password: 'xx xx' // 用户账户密码 + 8 位安全令牌
114
+ },
100
115
  /*
101
116
  【特别说明】以下配置项都自带默认值,非必填。如需自定义请自行配置。
102
117
  NODE_ENV: 'production',
103
118
  entry: { // 根据 src/components 目录下的文件自动生成 entry 相关配置
104
- InfoCardModel: './src/components/info-card/model.ts',
105
- infoCard: './src/components/info-card/register.ts'
119
+ InfoCardModel: './src/components/entity-form/model.ts',
120
+ infoCard: './src/components/entity-form/register.ts'
106
121
  },
107
122
  cssExtract: false, // 不额外提取css文件
108
- ossType: 'ali', // oss类型:ali、baidu
109
- ossConfig: {
110
- endpoint: 'https://oss-cn-beijing.aliyuncs.com',
111
- AccessKeyId: 'xxx',
112
- AccessKeySecret: 'xx',
113
- bucket: 'neo-widgets' // 存储桶名称
114
- },
115
123
  assetsRoot: resolve('dist') // 上传指定目录下的脚本文件
116
124
  */
117
125
  },
@@ -10,9 +10,9 @@
10
10
  "author": "wibetter",
11
11
  "license": "MIT",
12
12
  "scripts": {
13
- "preview": "neo preview --cmpType=data-dashboard",
13
+ "preview": "neo preview",
14
14
  "linkDebug": "neo linkDebug",
15
- "publish2oss": "neo publish2oss",
15
+ "pushCmp": "neo pushCmp",
16
16
  "format": "prettier --write \"src/**/**/*.{js,jsx,ts,tsx,vue,scss,json}\""
17
17
  },
18
18
  "files": [
@@ -47,7 +47,7 @@
47
47
  "@commitlint/config-conventional": "^9.1.1",
48
48
  "@types/react": "^16.9.11",
49
49
  "@types/react-dom": "^16.9.15",
50
- "neo-cmp-cli": "^1.3.8",
50
+ "neo-cmp-cli": "^1.5.3",
51
51
  "husky": "^4.2.5",
52
52
  "lint-staged": "^10.2.9",
53
53
  "prettier": "^2.0.5"
@@ -46,8 +46,8 @@ module.exports = {
46
46
  'chart-widget': path.resolve('./src/components/chart-widget'), // 导出图表自定义组件
47
47
  'neo-register': 'neo-register', // 导出 Neo 注册模块
48
48
  },
49
+ // remoteDeps: ['xxModule'], // 远程依赖组件,表示当前自定义组件会用到的远程依赖组件,需要和 neoExternals 配合使用
49
50
  // neoExternals: ['xxModule'], // 自定义组件中需要剔除的模块,仅支持数组写法
50
- // remotes: ['xxModule'], // 远程组件,表示当前自定义组件会用到的远程组件,需要和 neoExternals 配合使用
51
51
  },
52
52
  */
53
53
  preview: {