neo-cmp-cli 1.8.7 → 1.8.8
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.
- package/bin/index.js +2 -2
- package/dist/_virtual/_commonjsHelpers.js +12 -0
- package/dist/_virtual/array-set.js +7 -0
- package/dist/_virtual/base64-vlq.js +7 -0
- package/dist/_virtual/base64.js +7 -0
- package/dist/_virtual/binary-search.js +7 -0
- package/dist/_virtual/mapping-list.js +7 -0
- package/dist/_virtual/quick-sort.js +7 -0
- package/dist/_virtual/source-map-consumer.js +7 -0
- package/dist/_virtual/source-map-generator.js +7 -0
- package/dist/_virtual/source-map-support.js +7 -0
- package/dist/_virtual/source-map.js +7 -0
- package/dist/_virtual/source-node.js +7 -0
- package/dist/_virtual/typescript.js +7 -0
- package/dist/_virtual/util.js +7 -0
- package/dist/config/auth.config.js +43 -0
- package/dist/config/default.config.js +192 -0
- package/dist/config/index.js +26 -0
- package/dist/main.js +9 -0
- package/dist/main2.js +259 -0
- package/dist/module/inspect.js +63 -0
- package/dist/module/neoInit.js +75 -0
- package/dist/module/neoInitByCopy.js +83 -0
- package/dist/neo/neoLogin.js +590 -0
- package/dist/neo/neoRequire.js +123 -0
- package/dist/neo/neoService.js +898 -0
- package/dist/node_modules/buffer-from/index.js +86 -0
- package/dist/node_modules/source-map/lib/array-set.js +138 -0
- package/dist/node_modules/source-map/lib/base64-vlq.js +157 -0
- package/dist/node_modules/source-map/lib/base64.js +83 -0
- package/dist/node_modules/source-map/lib/binary-search.js +129 -0
- package/dist/node_modules/source-map/lib/mapping-list.js +96 -0
- package/dist/node_modules/source-map/lib/quick-sort.js +130 -0
- package/dist/node_modules/source-map/lib/source-map-consumer.js +1166 -0
- package/dist/node_modules/source-map/lib/source-map-generator.js +445 -0
- package/dist/node_modules/source-map/lib/source-node.js +431 -0
- package/dist/node_modules/source-map/lib/util.js +506 -0
- package/dist/node_modules/source-map/source-map.js +27 -0
- package/dist/node_modules/source-map-support/source-map-support.js +646 -0
- package/dist/node_modules/typescript/lib/typescript.js +174130 -0
- package/dist/oss/publish2oss.js +331 -0
- package/dist/plugins/AddNeoRequirePlugin.js +195 -0
- package/dist/utils/autoEntryRootDir.js +103 -0
- package/dist/utils/cmpUtils/createCmpByTemplate.js +82 -0
- package/dist/utils/cmpUtils/createCmpByZip.js +433 -0
- package/dist/utils/cmpUtils/createCommonModulesCode.js +139 -0
- package/dist/utils/cmpUtils/deleteCmp.js +81 -0
- package/dist/utils/cmpUtils/getCmpModelRegisterCode.js +47 -0
- package/dist/utils/cmpUtils/getCmpPreviewCode.js +60 -0
- package/dist/utils/cmpUtils/getCmpRegisterCode.js +47 -0
- package/dist/utils/cmpUtils/getCmpTypeByDir.js +59 -0
- package/dist/utils/cmpUtils/hasCmpTypeByDir.js +27 -0
- package/dist/utils/cmpUtils/previewCmp.js +75 -0
- package/dist/utils/cmpUtils/pullCmp.js +126 -0
- package/dist/utils/cmpUtils/pushCmp.js +254 -0
- package/dist/utils/common.js +125 -0
- package/dist/utils/configureNeoBuild.js +129 -0
- package/dist/utils/generateEntries.js +80 -0
- package/dist/utils/neoConfigInit.js +30 -0
- package/dist/utils/neoParams.js +26 -0
- package/dist/utils/pathUtils.js +40 -0
- package/dist/utils/projectNameValidator.js +90 -0
- package/dist/utils/projectUtils/createCmpProjectByTemplate.js +81 -0
- package/dist/utils/projectUtils/createCmpProjectZip.js +141 -0
- package/dist/utils/projectUtils/getEntries.js +99 -0
- package/dist/utils/projectUtils/getEntriesWithAutoRegister.js +129 -0
- package/dist/utils/projectUtils/hasNeoProject.js +34 -0
- package/dist/utils/projectUtils/openProject.js +117 -0
- package/dist/utils/projectUtils/updatePublishLog.js +47 -0
- package/dist/utils/replaceInFilesByMap.js +71 -0
- package/dist/utils/replaceInPackage.js +151 -0
- package/dist/utils/resetPackageVersion.js +132 -0
- package/package.json +6 -8
- package/test/demo.js +0 -2
- package/src/config/auth.config.js +0 -27
- package/src/config/default.config.js +0 -176
- package/src/config/index.js +0 -9
- package/src/initData/defaultTemplate.html +0 -13
- package/src/initData/neo.config.js +0 -138
- package/src/main.js +0 -221
- package/src/module/inspect.js +0 -41
- package/src/module/neoInit.js +0 -55
- package/src/module/neoInitByCopy.js +0 -61
- package/src/neo/NeoUMDContent.js +0 -30
- package/src/neo/neoLogin.js +0 -565
- package/src/neo/neoRequire.js +0 -125
- package/src/neo/neoService.js +0 -874
- package/src/neo/webpack.mf.js +0 -60
- package/src/neo/wrapperContent.js +0 -16
- package/src/oss/publish2oss.js +0 -348
- package/src/plugins/AddNeoRequirePlugin-v1.js +0 -47
- package/src/plugins/AddNeoRequirePlugin.js +0 -179
- package/src/plugins/README.md +0 -109
- package/src/utils/autoEntryRootDir.js +0 -85
- package/src/utils/cmpUtils/createCmpByTemplate.js +0 -60
- package/src/utils/cmpUtils/createCmpByZip.js +0 -408
- package/src/utils/cmpUtils/createCommonModulesCode.js +0 -121
- package/src/utils/cmpUtils/deleteCmp.js +0 -63
- package/src/utils/cmpUtils/getCmpModelRegisterCode.js +0 -31
- package/src/utils/cmpUtils/getCmpPreviewCode.js +0 -43
- package/src/utils/cmpUtils/getCmpRegisterCode.js +0 -31
- package/src/utils/cmpUtils/getCmpTypeByDir.js +0 -41
- package/src/utils/cmpUtils/hasCmpTypeByDir.js +0 -11
- package/src/utils/cmpUtils/previewCmp.js +0 -55
- package/src/utils/cmpUtils/pullCmp.js +0 -104
- package/src/utils/cmpUtils/pushCmp.js +0 -230
- package/src/utils/common.js +0 -107
- package/src/utils/configureNeoBuild.js +0 -109
- package/src/utils/generateEntries.js +0 -63
- package/src/utils/neoConfigInit.js +0 -13
- package/src/utils/neoParams.js +0 -12
- package/src/utils/pathUtils.js +0 -23
- package/src/utils/projectNameValidator.js +0 -76
- package/src/utils/projectUtils/createCmpProjectByTemplate.js +0 -59
- package/src/utils/projectUtils/createCmpProjectZip.js +0 -120
- package/src/utils/projectUtils/getEntries.js +0 -80
- package/src/utils/projectUtils/getEntriesWithAutoRegister.js +0 -108
- package/src/utils/projectUtils/hasNeoProject.js +0 -17
- package/src/utils/projectUtils/openProject.js +0 -96
- package/src/utils/projectUtils/updatePublishLog.js +0 -30
- package/src/utils/replaceInFiles.js +0 -47
- package/src/utils/replaceInFilesByMap.js +0 -54
- package/src/utils/replaceInPackage.js +0 -134
- package/src/utils/resetPackageVersion.js +0 -115
package/src/plugins/README.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# AddNeoRequirePlugin 使用说明
|
|
2
|
-
|
|
3
|
-
## 概述
|
|
4
|
-
|
|
5
|
-
`AddNeoRequirePlugin` 是一个 webpack 插件,用于在构建过程中自动为 JavaScript 文件注入 `neoRequire` 包装代码,实现与 Neo 平台的依赖共享。
|
|
6
|
-
|
|
7
|
-
## 使用方法
|
|
8
|
-
|
|
9
|
-
### 基本使用
|
|
10
|
-
|
|
11
|
-
```javascript
|
|
12
|
-
const AddNeoRequirePlugin = require('./AddNeoRequirePlugin');
|
|
13
|
-
|
|
14
|
-
module.exports = {
|
|
15
|
-
// ... 其他配置
|
|
16
|
-
plugins: [
|
|
17
|
-
new AddNeoRequirePlugin()
|
|
18
|
-
]
|
|
19
|
-
};
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### 高级配置
|
|
23
|
-
|
|
24
|
-
```javascript
|
|
25
|
-
const AddNeoRequirePlugin = require('./AddNeoRequirePlugin');
|
|
26
|
-
|
|
27
|
-
module.exports = {
|
|
28
|
-
// ... 其他配置
|
|
29
|
-
plugins: [
|
|
30
|
-
new AddNeoRequirePlugin({
|
|
31
|
-
// 启用详细日志
|
|
32
|
-
verbose: true,
|
|
33
|
-
|
|
34
|
-
// 跳过已注入的文件(默认: true)
|
|
35
|
-
skipInjected: true,
|
|
36
|
-
|
|
37
|
-
// 需要跳过的文件扩展名
|
|
38
|
-
skipExtensions: ['.css', '.map', '.txt', '.html', '.svg'],
|
|
39
|
-
|
|
40
|
-
// 需要跳过的文件名模式(正则表达式)
|
|
41
|
-
skipPatterns: [
|
|
42
|
-
/\.map$/,
|
|
43
|
-
/\.LICENSE\.txt$/,
|
|
44
|
-
/\.html$/,
|
|
45
|
-
/vendor\./,
|
|
46
|
-
/chunk\./
|
|
47
|
-
]
|
|
48
|
-
})
|
|
49
|
-
]
|
|
50
|
-
};
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## 配置选项
|
|
54
|
-
|
|
55
|
-
| 选项 | 类型 | 默认值 | 说明 |
|
|
56
|
-
|------|------|--------|------|
|
|
57
|
-
| `verbose` | boolean | false | 是否启用详细日志输出 |
|
|
58
|
-
| `skipInjected` | boolean | true | 是否跳过已经注入过的文件 |
|
|
59
|
-
| `skipExtensions` | string[] | ['.css', '.map', '.txt', '.html'] | 需要跳过的文件扩展名列表 |
|
|
60
|
-
| `skipPatterns` | RegExp[] | [/\.map$/, /\.LICENSE\.txt$/, /\.html$/] | 需要跳过的文件名模式 |
|
|
61
|
-
|
|
62
|
-
## 注入的代码结构
|
|
63
|
-
|
|
64
|
-
插件会为每个 JavaScript 文件添加以下包装代码:
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
(function(NeoCustomCmpFileFactory) {
|
|
68
|
-
if (!window.neoRequire) {
|
|
69
|
-
throw new Error('neoRequire 不存在,请在 NeoCRM 平台中加载此脚本。');
|
|
70
|
-
}
|
|
71
|
-
NeoCustomCmpFileFactory(window.neoRequire);
|
|
72
|
-
})(function(require) {
|
|
73
|
-
// 原始文件内容
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## 日志输出示例
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
[AddNeoRequirePlugin] 开始处理资源文件...
|
|
81
|
-
[AddNeoRequirePlugin] 跳过文件: main.css (匹配跳过规则)
|
|
82
|
-
[AddNeoRequirePlugin] 跳过文件: main.js.map (匹配跳过规则)
|
|
83
|
-
[AddNeoRequirePlugin] 成功处理文件: main.js
|
|
84
|
-
[AddNeoRequirePlugin] 成功处理文件: chunk.1.js
|
|
85
|
-
[AddNeoRequirePlugin] 处理完成 - 成功: 2, 跳过: 2, 错误: 0
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## 故障排除
|
|
89
|
-
|
|
90
|
-
### 1. 文件未被注入
|
|
91
|
-
- 检查文件是否匹配跳过规则
|
|
92
|
-
- 启用 `verbose: true` 查看详细日志
|
|
93
|
-
- 确认文件内容有效且不为空
|
|
94
|
-
|
|
95
|
-
### 2. 重复注入
|
|
96
|
-
- 启用 `skipInjected: true` 选项
|
|
97
|
-
- 检查文件是否已经被处理过
|
|
98
|
-
|
|
99
|
-
### 3. 构建错误
|
|
100
|
-
- 查看错误日志中的详细错误信息
|
|
101
|
-
- 检查文件内容是否包含特殊字符
|
|
102
|
-
- 确认 webpack 版本兼容性
|
|
103
|
-
|
|
104
|
-
## 注意事项
|
|
105
|
-
|
|
106
|
-
1. 插件会在 webpack 的 `processAssets` 阶段执行,优先级为 -100
|
|
107
|
-
2. 只处理 JavaScript 文件,自动跳过 CSS、Map 等非 JS 文件
|
|
108
|
-
3. 支持 SourceMap 文件,但不会修改它们
|
|
109
|
-
4. 建议在生产环境中关闭详细日志以提高性能
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const openProject = require('./projectUtils/openProject');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 自动进入指定项目的根目录
|
|
7
|
-
*
|
|
8
|
-
* ⚠️ 重要说明:子进程无法直接改变父 shell 的工作目录
|
|
9
|
-
*
|
|
10
|
-
* 原因:
|
|
11
|
-
* 1. 进程隔离:每个进程都有独立的工作目录,子进程无法修改父进程的工作目录
|
|
12
|
-
* 2. Shell 限制:cd 是 shell 的内置命令,必须在父 shell 进程中执行
|
|
13
|
-
* 3. 安全机制:操作系统禁止子进程修改父进程的工作目录,防止安全问题
|
|
14
|
-
*
|
|
15
|
-
* 解决方案:
|
|
16
|
-
* 1. 使用 process.chdir() 改变 Node.js 进程的工作目录(仅对后续 Node.js 操作有效)
|
|
17
|
-
* 2. 输出 shell 命令让用户执行以改变 shell 的工作目录
|
|
18
|
-
*
|
|
19
|
-
* @param {string} projectPath 项目路径(可以是相对路径或绝对路径)
|
|
20
|
-
* @param {object} options 配置选项
|
|
21
|
-
* @param {boolean} options.outputShellCommand 是否输出 shell 命令让用户执行(默认:true)
|
|
22
|
-
* @returns {boolean} 是否成功切换目录
|
|
23
|
-
*/
|
|
24
|
-
module.exports = function (projectPath, options = {}) {
|
|
25
|
-
const { outputShellCommand = true } = options;
|
|
26
|
-
|
|
27
|
-
if (!projectPath) {
|
|
28
|
-
console.error('运行异常:未找到可用的项目路径。');
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 将路径解析为绝对路径
|
|
33
|
-
const absolutePath = path.isAbsolute(projectPath)
|
|
34
|
-
? projectPath
|
|
35
|
-
: path.resolve(process.cwd(), projectPath);
|
|
36
|
-
|
|
37
|
-
// 检查目录是否存在
|
|
38
|
-
if (!fs.existsSync(absolutePath)) {
|
|
39
|
-
console.error(`运行异常:${projectPath} 目录不存在。`);
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// 检查是否为目录
|
|
44
|
-
const stats = fs.statSync(absolutePath);
|
|
45
|
-
if (!stats.isDirectory()) {
|
|
46
|
-
console.error(`运行异常:${projectPath} 不是目录。`);
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const projectName = path.relative(process.cwd(), absolutePath);
|
|
51
|
-
|
|
52
|
-
let success = false;
|
|
53
|
-
|
|
54
|
-
// 改变 Node.js 进程的工作目录(仅对后续 Node.js 操作有效)
|
|
55
|
-
try {
|
|
56
|
-
process.chdir(absolutePath);
|
|
57
|
-
success = true;
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.error(`错误:无法切换到目录 ${projectName}: ${error.message}`);
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// 输出 shell 命令让用户执行(这样可以真正改变 shell 的工作目录)
|
|
64
|
-
if (outputShellCommand) {
|
|
65
|
-
// 输出提示信息
|
|
66
|
-
console.log('\n' + '='.repeat(60));
|
|
67
|
-
console.log('💡 提示:要切换到新建项目目录,请执行以下命令:');
|
|
68
|
-
console.log(`\n cd ${projectName} \n`);
|
|
69
|
-
console.log('='.repeat(60));
|
|
70
|
-
console.log('\n📝 说明1:组件开发工具(neo-cmp-cli)无法直接改变当前命令窗口工作目录,');
|
|
71
|
-
console.log(` 您需要手动执行上述命令才能进入刚创建的自定义组件项目(${projectName})。`);
|
|
72
|
-
console.log(
|
|
73
|
-
`\n📝 说明2:组件开发工具(neo-cmp-cli)默认自动打开编辑器(cursor 或 vscode): neo open -n ${projectName}。\n`
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// 自动打开 IDE编辑器(异步执行,不需要等待)
|
|
77
|
-
openProject('auto', projectPath).catch((error) => {
|
|
78
|
-
// 错误已在 openProject 内部处理,这里只是防止未处理的 Promise 警告
|
|
79
|
-
console.error(`自动打开编辑器出错(neo open -n ${projectName}): ${error.message}`);
|
|
80
|
-
console.error(`请手动执行命令 neo open -n ${projectName} 打开编辑器。`);
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return success;
|
|
85
|
-
};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
const { consoleTag } = require('../neoParams'); // 输出标记
|
|
5
|
-
const replaceInFilesByMap = require('../replaceInFilesByMap');
|
|
6
|
-
const hasCmpTypeByDir = require('./hasCmpTypeByDir');
|
|
7
|
-
const hasNeoProject = require('../projectUtils/hasNeoProject');
|
|
8
|
-
|
|
9
|
-
// 自定义组件内容模板信息
|
|
10
|
-
const curCmpTemplate = {
|
|
11
|
-
// 需要替换掉的字段信息(模板中的字段)
|
|
12
|
-
widgetInfo: {
|
|
13
|
-
cmpName: 'CustomCmp',
|
|
14
|
-
modelName: 'CmpModel',
|
|
15
|
-
cmpClassName: 'custom-cmp-container',
|
|
16
|
-
cmpType: 'xx-custom-cmp',
|
|
17
|
-
cmpLabel: 'xx组件'
|
|
18
|
-
},
|
|
19
|
-
dir: path.resolve(__dirname, '../../../template/empty-cmp')
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 创建自定义组件
|
|
24
|
-
* @param {*} cmpName 自定义组件名称
|
|
25
|
-
*/
|
|
26
|
-
module.exports = function (cmpName, componentBaseDir = './src/components') {
|
|
27
|
-
const currentTemplateDir = curCmpTemplate.dir;
|
|
28
|
-
const finalCmpName = cmpName || 'neoCustomCmp';
|
|
29
|
-
const finalCmpPath = path.resolve(process.cwd(), componentBaseDir, finalCmpName);
|
|
30
|
-
|
|
31
|
-
if (!hasNeoProject()) {
|
|
32
|
-
console.error(
|
|
33
|
-
`${consoleTag}当前(${process.cwd()})还不是自定义组件项目,请先创建一个自定义组件项目(neo init / neo create project)。`
|
|
34
|
-
);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (hasCmpTypeByDir(finalCmpName)) {
|
|
39
|
-
console.error(`${consoleTag}创建自定义组件失败,当前项目已经存在${finalCmpName}自定义组件。`);
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
fs.copy(currentTemplateDir, finalCmpPath)
|
|
44
|
-
.then(() => {
|
|
45
|
-
const curCmpName = _.camelCase(finalCmpName);
|
|
46
|
-
const cmpType = _.kebabCase(finalCmpName);
|
|
47
|
-
|
|
48
|
-
// 替换文件中的内容
|
|
49
|
-
replaceInFilesByMap(finalCmpPath, {
|
|
50
|
-
[curCmpTemplate.widgetInfo.modelName]: `${curCmpName}Model`,
|
|
51
|
-
[curCmpTemplate.widgetInfo.cmpName]: curCmpName,
|
|
52
|
-
[curCmpTemplate.widgetInfo.cmpClassName]: `${cmpType}-container`,
|
|
53
|
-
[curCmpTemplate.widgetInfo.cmpType]: cmpType,
|
|
54
|
-
[curCmpTemplate.widgetInfo.cmpLabel]: `${cmpType}组件`
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
console.log(`${consoleTag}已创建自定义组件(${finalCmpName})!`);
|
|
58
|
-
})
|
|
59
|
-
.catch((err) => console.error(`${consoleTag}自定义组件创建失败(${finalCmpName}):`, err));
|
|
60
|
-
};
|
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const axios = require('axios');
|
|
4
|
-
const ora = require('ora');
|
|
5
|
-
const AdmZip = require('adm-zip');
|
|
6
|
-
const _ = require('lodash');
|
|
7
|
-
const { consoleTag } = require('../neoParams'); // 输出标记
|
|
8
|
-
const hasCmpTypeByDir = require('./hasCmpTypeByDir');
|
|
9
|
-
const hasNeoProject = require('../projectUtils/hasNeoProject');
|
|
10
|
-
const { errorLog, warningLog, successLog, parseTsConfigWithTypeScript } = require('../common');
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 检查对象键名是否需要加引号
|
|
14
|
-
* @param {string} key 键名
|
|
15
|
-
* @returns {boolean} 是否需要加引号
|
|
16
|
-
*/
|
|
17
|
-
function needsQuotes(key) {
|
|
18
|
-
// 如果键名包含特殊字符(如 @、-、空格等)或不是有效的 JavaScript 标识符,需要加引号
|
|
19
|
-
// 有效的 JavaScript 标识符:以字母、$、_ 开头,后续可以是字母、数字、$、_
|
|
20
|
-
const validIdentifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
21
|
-
return !validIdentifierRegex.test(key);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 格式化对象键名,如果需要则加引号
|
|
26
|
-
* @param {string} key 键名
|
|
27
|
-
* @returns {string} 格式化后的键名
|
|
28
|
-
*/
|
|
29
|
-
function formatKey(key) {
|
|
30
|
-
if (needsQuotes(key)) {
|
|
31
|
-
return JSON.stringify(key);
|
|
32
|
-
}
|
|
33
|
-
return key;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 将配置对象格式化为 JavaScript 代码字符串
|
|
38
|
-
* @param {*} obj 配置对象
|
|
39
|
-
* @param {number} indent 缩进级别
|
|
40
|
-
* @param {string} fileDir 配置文件所在目录路径,用于将绝对路径转换回相对路径(resolve() 基于 __dirname)
|
|
41
|
-
* @returns {string} 格式化后的字符串
|
|
42
|
-
*/
|
|
43
|
-
function formatConfigObject(obj, indent = 0, fileDir = '') {
|
|
44
|
-
const indentStr = ' '.repeat(indent);
|
|
45
|
-
const nextIndentStr = ' '.repeat(indent + 1);
|
|
46
|
-
|
|
47
|
-
if (obj === null) {
|
|
48
|
-
return 'null';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (obj === undefined) {
|
|
52
|
-
return 'undefined';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (typeof obj === 'string') {
|
|
56
|
-
// 尝试将绝对路径转换回 resolve() 调用
|
|
57
|
-
if (fileDir && path.isAbsolute(obj)) {
|
|
58
|
-
try {
|
|
59
|
-
const relativePath = path.relative(fileDir, obj).replace(/\\/g, '/');
|
|
60
|
-
// 如果路径看起来像是通过 resolve() 生成的,转换回 resolve() 调用
|
|
61
|
-
// 排除包含 .. 的路径(这些可能是外部路径)
|
|
62
|
-
if (relativePath && !relativePath.startsWith('..') && relativePath !== '') {
|
|
63
|
-
// 确保路径使用正斜杠,并添加 ./ 前缀(如果需要)
|
|
64
|
-
const normalizedPath = relativePath.startsWith('.') ? relativePath : './' + relativePath;
|
|
65
|
-
return `resolve(${JSON.stringify(normalizedPath)})`;
|
|
66
|
-
}
|
|
67
|
-
} catch (e) {
|
|
68
|
-
// 如果路径转换失败,继续使用原始字符串
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// 检查是否包含 resolve() 或 auth. 调用(从原始文件中提取的)
|
|
72
|
-
if (obj.includes('resolve(') || obj.includes('auth.')) {
|
|
73
|
-
return obj;
|
|
74
|
-
}
|
|
75
|
-
// 转义字符串中的特殊字符
|
|
76
|
-
return JSON.stringify(obj);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (typeof obj === 'number' || typeof obj === 'boolean') {
|
|
80
|
-
return String(obj);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (Array.isArray(obj)) {
|
|
84
|
-
if (obj.length === 0) {
|
|
85
|
-
return '[]';
|
|
86
|
-
}
|
|
87
|
-
const items = obj.map((item) => {
|
|
88
|
-
const formatted = formatConfigObject(item, indent + 1, fileDir);
|
|
89
|
-
return nextIndentStr + formatted;
|
|
90
|
-
});
|
|
91
|
-
return '[\n' + items.join(',\n') + '\n' + indentStr + ']';
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (typeof obj === 'object') {
|
|
95
|
-
const keys = Object.keys(obj);
|
|
96
|
-
if (keys.length === 0) {
|
|
97
|
-
return '{}';
|
|
98
|
-
}
|
|
99
|
-
const items = keys.map((key) => {
|
|
100
|
-
const value = obj[key];
|
|
101
|
-
const formattedValue = formatConfigObject(value, indent + 1, fileDir);
|
|
102
|
-
return nextIndentStr + formatKey(key) + ': ' + formattedValue;
|
|
103
|
-
});
|
|
104
|
-
return '{\n' + items.join(',\n') + '\n' + indentStr + '}';
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 对于函数或其他类型,尝试转换为字符串
|
|
108
|
-
return String(obj);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* 从 zip 包中创建自定义组件
|
|
113
|
-
* @param {*} cmpZipUrl 自定义组件源码文件地址(zip包地址)
|
|
114
|
-
* @param {*} cmpName 自定义组件名称
|
|
115
|
-
* @param {*} componentBaseDir 自定义组件目录
|
|
116
|
-
*
|
|
117
|
-
* 拉取线上自定义组件源码 规则:
|
|
118
|
-
* 1、对于 组件源码中 src/components 中的所有文件 copy 至 当前项目 src/components,非覆盖式(copy 前判断是否存在);
|
|
119
|
-
* 2、对于 组件源码中的 配置文件(neo.config.js)内容 和 当前项目配置 进行 非覆盖式 merge 处理;
|
|
120
|
-
* 3、对于 组件源码中的 tsconfig.json 文件内容 和 当前项目 tsconfig 进行 非覆盖式 merge 处理;
|
|
121
|
-
* 4、对于 组件源码中的 package.json 文件内容 和 当前项目 package.json 进行 非覆盖式 merge 处理,并识别新增 依赖,提示用户 重新安装依赖
|
|
122
|
-
*/
|
|
123
|
-
async function createCmpByZip(cmpZipUrl, option = {}) {
|
|
124
|
-
const { token, cmpName, componentBaseDir = './src/components' } = option || {};
|
|
125
|
-
const finalCmpName = cmpName;
|
|
126
|
-
|
|
127
|
-
if (!hasNeoProject()) {
|
|
128
|
-
errorLog(
|
|
129
|
-
`当前(${process.cwd()})还不是自定义组件项目,请先创建一个自定义组件项目(neo init / neo create project)。`
|
|
130
|
-
);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (hasCmpTypeByDir(finalCmpName)) {
|
|
135
|
-
errorLog(`当前项目已经存在${finalCmpName}自定义组件。`);
|
|
136
|
-
process.exit(1);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// 创建临时目录
|
|
140
|
-
const tempDir = path.join(process.cwd(), '.neo-cli', 'zip-source');
|
|
141
|
-
|
|
142
|
-
const spinner = ora(`${consoleTag}正在下载组件源码...`).start();
|
|
143
|
-
|
|
144
|
-
// 下载源码文件并解析到 src/components 目录下
|
|
145
|
-
try {
|
|
146
|
-
await fs.ensureDir(tempDir); // 如果目录不存在,会自动创建(包括所有必要的父目录)
|
|
147
|
-
|
|
148
|
-
const zipFilePath = path.join(tempDir, `${finalCmpName}.zip`);
|
|
149
|
-
|
|
150
|
-
// 检查是否存在同名 zip 包,如果存在则先删除
|
|
151
|
-
if (await fs.pathExists(zipFilePath)) {
|
|
152
|
-
await fs.remove(zipFilePath);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// 下载 zip 文件
|
|
156
|
-
let response;
|
|
157
|
-
try {
|
|
158
|
-
response = await axios.get(cmpZipUrl, {
|
|
159
|
-
headers: {
|
|
160
|
-
Authorization: `Bearer ${token}`,
|
|
161
|
-
'xsy-inner-source': 'bff'
|
|
162
|
-
},
|
|
163
|
-
responseType: 'arraybuffer',
|
|
164
|
-
timeout: 60000, // 60秒超时
|
|
165
|
-
maxContentLength: Infinity,
|
|
166
|
-
maxBodyLength: Infinity,
|
|
167
|
-
maxRedirects: 5, // 支持重定向
|
|
168
|
-
validateStatus: function (status) {
|
|
169
|
-
// 接受 2xx 和 3xx 状态码(重定向)
|
|
170
|
-
return status >= 200 && status < 400;
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
} catch (axiosError) {
|
|
174
|
-
const errorMessage = axiosError.response
|
|
175
|
-
? `下载文件失败: HTTP ${axiosError.response.status} - ${axiosError.message}`
|
|
176
|
-
: `下载文件失败: ${axiosError.message}`;
|
|
177
|
-
errorLog(errorMessage, spinner);
|
|
178
|
-
throw axiosError;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// 保存 zip 文件到临时目录
|
|
182
|
-
try {
|
|
183
|
-
let buffer;
|
|
184
|
-
// 处理不同的数据类型
|
|
185
|
-
if (Buffer.isBuffer(response.data)) {
|
|
186
|
-
// 如果已经是 Buffer,直接使用
|
|
187
|
-
buffer = response.data;
|
|
188
|
-
} else if (response.data instanceof ArrayBuffer) {
|
|
189
|
-
// 如果是 ArrayBuffer,转换为 Buffer
|
|
190
|
-
buffer = Buffer.from(response.data);
|
|
191
|
-
} else if (response.data.buffer instanceof ArrayBuffer) {
|
|
192
|
-
// 如果是 TypedArray (如 Uint8Array),使用其 buffer
|
|
193
|
-
buffer = Buffer.from(response.data.buffer);
|
|
194
|
-
} else {
|
|
195
|
-
// 尝试直接转换
|
|
196
|
-
buffer = Buffer.from(response.data);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
await fs.writeFile(zipFilePath, buffer);
|
|
200
|
-
} catch (writeError) {
|
|
201
|
-
errorLog(`读取组件源码文件失败: ${writeError.message}`, spinner);
|
|
202
|
-
throw writeError;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// 解压 zip 文件
|
|
206
|
-
spinner.info(`${consoleTag}正在解压组件源码...`);
|
|
207
|
-
|
|
208
|
-
const zip = new AdmZip(zipFilePath);
|
|
209
|
-
// 解压后的目录名称为组件名称:.neo-cli/zip-source/xxCmp
|
|
210
|
-
const extractPath = path.join(tempDir, finalCmpName);
|
|
211
|
-
zip.extractAllTo(extractPath, true); // 解压到临时目录
|
|
212
|
-
|
|
213
|
-
// 查找解压后的 src/components 目录
|
|
214
|
-
const cmpSourcePath = path.join(extractPath, componentBaseDir, finalCmpName);
|
|
215
|
-
|
|
216
|
-
if (!fs.existsSync(cmpSourcePath)) {
|
|
217
|
-
errorLog(`解压后的 zip 包中未找到 ${finalCmpName} 组件源码目录。`, spinner);
|
|
218
|
-
// 只删除解压目录,保留 zip 源文件
|
|
219
|
-
await fs.remove(extractPath);
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// 确保目标目录存在
|
|
224
|
-
const targetComponentsDir = path.resolve(process.cwd(), componentBaseDir, finalCmpName);
|
|
225
|
-
await fs.ensureDir(targetComponentsDir);
|
|
226
|
-
|
|
227
|
-
// 复制 src/components 目录下的所有内容到目标目录
|
|
228
|
-
try {
|
|
229
|
-
await fs.copy(cmpSourcePath, targetComponentsDir);
|
|
230
|
-
} catch (err) {
|
|
231
|
-
errorLog(`自定义组件模板下载失败:${err.message || err}`, spinner);
|
|
232
|
-
// 只删除解压目录,保留 zip 源文件
|
|
233
|
-
await fs.remove(extractPath);
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// 处理源码中其他文件
|
|
238
|
-
try {
|
|
239
|
-
// 标准化 componentBaseDir,去掉开头的 ./ 并统一路径分隔符
|
|
240
|
-
const normalizedComponentBaseDir = componentBaseDir.replace(/^\.\//, '').replace(/\\/g, '/');
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* 递归复制文件,排除 componentBaseDir 目录
|
|
244
|
-
* @param {string} sourceDir 源目录
|
|
245
|
-
* @param {string} targetRoot 目标根目录(项目根目录)
|
|
246
|
-
* @param {string} excludeDir 要排除的目录(相对于源目录)
|
|
247
|
-
*/
|
|
248
|
-
const copyOtherFiles = async (sourceDir, targetRoot, excludeDir) => {
|
|
249
|
-
const items = await fs.readdir(sourceDir);
|
|
250
|
-
|
|
251
|
-
for (const item of items) {
|
|
252
|
-
const sourcePath = path.join(sourceDir, item); // 当前源文件路径
|
|
253
|
-
const stat = await fs.stat(sourcePath); // 当前源文件状态
|
|
254
|
-
const fileName = path.basename(sourcePath); // 当前源文件名称
|
|
255
|
-
|
|
256
|
-
// 计算相对于解压根目录的路径
|
|
257
|
-
const relativePath = path.relative(extractPath, sourcePath);
|
|
258
|
-
const normalizedRelativePath = relativePath.replace(/\\/g, '/');
|
|
259
|
-
|
|
260
|
-
// 跳过 componentBaseDir 目录及其子目录
|
|
261
|
-
if (
|
|
262
|
-
normalizedRelativePath.startsWith(excludeDir + '/') ||
|
|
263
|
-
normalizedRelativePath === excludeDir
|
|
264
|
-
) {
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const targetPath = path.join(targetRoot, relativePath); // 目标文件路径
|
|
269
|
-
|
|
270
|
-
if (stat.isDirectory()) {
|
|
271
|
-
// 如果是目录,递归处理
|
|
272
|
-
await copyOtherFiles(sourcePath, targetRoot, excludeDir);
|
|
273
|
-
} else if (stat.isFile()) {
|
|
274
|
-
// 如果是文件,检查目标文件是否存在
|
|
275
|
-
const targetExists = await fs.pathExists(targetPath);
|
|
276
|
-
if (!targetExists) {
|
|
277
|
-
// 确保目标目录存在
|
|
278
|
-
await fs.ensureDir(path.dirname(targetPath));
|
|
279
|
-
// 复制文件
|
|
280
|
-
await fs.copy(sourcePath, targetPath);
|
|
281
|
-
} else if (fileName === 'package.json') {
|
|
282
|
-
// 处理 package.json 文件(当目标文件已存在时)
|
|
283
|
-
const sourcePackageJson = await fs.readJson(sourcePath);
|
|
284
|
-
const targetPackageJson = await fs.readJson(targetPath);
|
|
285
|
-
|
|
286
|
-
const newPackageJsonDeps = _.omit(
|
|
287
|
-
sourcePackageJson.dependencies,
|
|
288
|
-
Object.keys(targetPackageJson.dependencies)
|
|
289
|
-
);
|
|
290
|
-
const newDeps = Object.keys(newPackageJsonDeps);
|
|
291
|
-
if (newDeps.length > 0) {
|
|
292
|
-
warningLog(
|
|
293
|
-
`检测到 package.json 中新增了 ${newDeps.length} 个依赖包:${newDeps.join(', ')}`
|
|
294
|
-
);
|
|
295
|
-
warningLog(
|
|
296
|
-
`为确保组件正常运行,请执行以下命令安装依赖:npm install 或 yarn install`
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
// 合并 package.json:将源文件的配置合并到目标文件中
|
|
300
|
-
// 注意:_.merge 会修改第一个参数,所以先克隆目标对象
|
|
301
|
-
const mergedPackageJson = _.merge({}, sourcePackageJson, targetPackageJson);
|
|
302
|
-
await fs.writeJson(targetPath, mergedPackageJson, { spaces: 2 });
|
|
303
|
-
} else if (fileName === 'neo.config.js') {
|
|
304
|
-
// 处理 neo.config.js 文件(当目标文件已存在时)
|
|
305
|
-
try {
|
|
306
|
-
// 清除 require 缓存,确保读取最新内容
|
|
307
|
-
const sourceResolvedPath = require.resolve(sourcePath);
|
|
308
|
-
const targetResolvedPath = require.resolve(targetPath);
|
|
309
|
-
delete require.cache[sourceResolvedPath];
|
|
310
|
-
delete require.cache[targetResolvedPath];
|
|
311
|
-
|
|
312
|
-
// 读取源文件和目标文件的原始内容
|
|
313
|
-
const sourceContent = await fs.readFile(sourcePath, 'utf8');
|
|
314
|
-
const targetContent = await fs.readFile(targetPath, 'utf8');
|
|
315
|
-
|
|
316
|
-
// 读取源文件和目标文件的配置对象(函数调用会被执行)
|
|
317
|
-
const sourceConfig = require(sourcePath);
|
|
318
|
-
const targetConfig = require(targetPath);
|
|
319
|
-
|
|
320
|
-
// 合并配置:源配置合并到目标配置(目标配置优先级更高)
|
|
321
|
-
const mergedConfig = _.merge({}, sourceConfig, targetConfig);
|
|
322
|
-
|
|
323
|
-
// 提取目标文件的头部(module.exports 之前的内容)
|
|
324
|
-
const moduleExportMatch = targetContent.match(
|
|
325
|
-
/^([\s\S]*?)(module\.exports\s*=\s*\{[\s\S]*\};?\s*)$/
|
|
326
|
-
);
|
|
327
|
-
let fileHeader = '';
|
|
328
|
-
if (moduleExportMatch) {
|
|
329
|
-
fileHeader = moduleExportMatch[1];
|
|
330
|
-
} else {
|
|
331
|
-
// 如果没有找到 module.exports,尝试提取到最后一个大括号之前的内容
|
|
332
|
-
const lastBraceIndex = targetContent.lastIndexOf('module.exports');
|
|
333
|
-
if (lastBraceIndex > 0) {
|
|
334
|
-
fileHeader = targetContent.substring(0, lastBraceIndex);
|
|
335
|
-
} else {
|
|
336
|
-
// 如果都没有找到,使用默认头部
|
|
337
|
-
fileHeader =
|
|
338
|
-
"'use strict';\nconst path = require('path');\n\n// 统一路径解析\nfunction resolve(dir) {\n return path.resolve(__dirname, dir);\n}\n\n";
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// 获取目标文件的目录,用于路径转换(resolve() 基于 __dirname)
|
|
343
|
-
const targetFileDir = path.dirname(targetPath);
|
|
344
|
-
|
|
345
|
-
// 将合并后的配置对象转换为格式化的字符串
|
|
346
|
-
const configString = formatConfigObject(mergedConfig, 0, targetFileDir);
|
|
347
|
-
|
|
348
|
-
// 组合完整的文件内容
|
|
349
|
-
const mergedContent = fileHeader + 'module.exports = ' + configString + ';\n';
|
|
350
|
-
|
|
351
|
-
// 写回文件
|
|
352
|
-
await fs.writeFile(targetPath, mergedContent, 'utf8');
|
|
353
|
-
} catch (configError) {
|
|
354
|
-
spinner.warn(
|
|
355
|
-
`${consoleTag}合并 neo.config.js 配置文件时出现警告:${
|
|
356
|
-
configError.message || configError
|
|
357
|
-
}`
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
} else if (fileName === 'tsconfig.json') {
|
|
361
|
-
// 处理 tsconfig.json 文件(当目标文件已存在时)
|
|
362
|
-
try {
|
|
363
|
-
const sourceTsconfigJson = parseTsConfigWithTypeScript(sourcePath);
|
|
364
|
-
// 先判断 targetPath 是否存在,如果存在则合并,否则直接写入
|
|
365
|
-
if (await fs.pathExists(targetPath)) {
|
|
366
|
-
const targetTsconfigJson = parseTsConfigWithTypeScript(targetPath);
|
|
367
|
-
// 合并配置:源配置合并到目标配置(目标配置优先级更高)
|
|
368
|
-
const mergedTsconfigJson = _.merge({}, sourceTsconfigJson, targetTsconfigJson);
|
|
369
|
-
// 使用 writeJson 写入,确保 JSON 格式正确(所有键名都会被正确序列化)
|
|
370
|
-
await fs.writeJson(targetPath, mergedTsconfigJson, { spaces: 2 });
|
|
371
|
-
} else {
|
|
372
|
-
await fs.writeJson(targetPath, sourceTsconfigJson, { spaces: 2 });
|
|
373
|
-
}
|
|
374
|
-
} catch (tsconfigError) {
|
|
375
|
-
spinner.warn(
|
|
376
|
-
`${consoleTag}合并 tsconfig.json 配置文件时出现警告:${
|
|
377
|
-
tsconfigError.message || tsconfigError
|
|
378
|
-
}`
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
// 开始处理源码中其他文件
|
|
386
|
-
await copyOtherFiles(extractPath, process.cwd(), normalizedComponentBaseDir);
|
|
387
|
-
} catch (err) {
|
|
388
|
-
spinner.warn(`${consoleTag}处理源码文件出现警告:${err.message || err}`);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// 清理解压目录,保留 zip 源文件
|
|
392
|
-
await fs.remove(extractPath);
|
|
393
|
-
successLog(`已成功从 zip 包解析自定义组件(${finalCmpName})!`, spinner);
|
|
394
|
-
|
|
395
|
-
return true;
|
|
396
|
-
} catch (error) {
|
|
397
|
-
errorLog(`从 zip 包创建自定义组件失败(${finalCmpName}):${error.message || error}`, spinner);
|
|
398
|
-
// 清理解压目录,保留 zip 源文件
|
|
399
|
-
const extractPath = path.join(tempDir, finalCmpName);
|
|
400
|
-
if (await fs.pathExists(extractPath)) {
|
|
401
|
-
await fs.remove(extractPath);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
module.exports = createCmpByZip;
|