neo-cmp-cli 1.2.9 → 1.2.11
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/README.md +20 -14
- package/package.json +1 -1
- package/src/cmpUtils/getEntries.js +8 -2
- package/src/cmpUtils/getEntriesWithAutoRegister.js +8 -2
- package/src/cmpUtils/updatePublishLog.js +30 -0
- package/src/module/index.js +7 -3
- package/src/module/main.js +3 -4
- package/src/neo/wrapperContent.js +2 -2
- package/src/oss/publish2oss.js +3 -0
- package/src/plugins/AddNeoRequirePlugin-v1.js +47 -0
- package/src/plugins/AddNeoRequirePlugin.js +153 -18
- package/src/plugins/README.md +109 -2
- package/src/template/neo-custom-cmp-template/package.json +1 -1
- package/src/template/neo-custom-cmp-template/src/components/contact-form/README.md +1 -1
- package/src/template/neo-custom-cmp-template/src/components/xobject-table/README.md +0 -5
- package/src/template/neo-custom-cmp-template/src/components/xobject-table/index.tsx +8 -12
- package/src/template/neo-custom-cmp-template/src/components/xobject-table-v2/README.md +0 -5
- package/src/template/neo-custom-cmp-template/src/components/xobject-table-v2/index.tsx +8 -12
package/README.md
CHANGED
|
@@ -13,7 +13,10 @@ neo-cmp-cli 是 Neo 自定义组件开发工具,基于 [AKFun](https://github.
|
|
|
13
13
|
### 模板类型(neo init 可选)
|
|
14
14
|
- **React 模板**: [react-custom-cmp-template](https://github.com/wibetter/react-custom-cmp-template)
|
|
15
15
|
- **React + TS 模板**: [react-ts-custom-cmp-template](https://github.com/wibetter/react-ts-custom-cmp-template)
|
|
16
|
-
- **
|
|
16
|
+
- **Antd 模板**: [antd-custom-cmp-template](https://github.com/wibetter/antd-custom-cmp-template) Antd 自定义组件
|
|
17
|
+
- **Neo 模板**: [neo-custom-cmp-template](https://github.com/wibetter/neo-custom-cmp-template) 支持使用 Neo 实体数据源(内置 Neo Open API 使用示例)
|
|
18
|
+
- **Echarts 模板**: [echarts-custom-cmp-template](https://github.com/wibetter/echarts-custom-cmp-template) Echarts 图表自定义组件
|
|
19
|
+
- **Vue2 模板**: [vue2-custom-cmp-template](https://github.com/wibetter/vue2-custom-cmp-template) Vue2.0 自定义组件
|
|
17
20
|
|
|
18
21
|
## 快速开始
|
|
19
22
|
|
|
@@ -68,11 +71,24 @@ npm run publish2oss
|
|
|
68
71
|
```
|
|
69
72
|
|
|
70
73
|
## 常用命令
|
|
71
|
-
- **neo init**:
|
|
74
|
+
- **neo init**: 交互式创建自定义组件(支持 -t、--name)。
|
|
72
75
|
- **neo preview**: 本地预览自定义组件内容,默认支持热更新与接口代理。
|
|
73
|
-
- **neo linkDebug**:
|
|
76
|
+
- **neo linkDebug**: 外链调试模式,在平台端页面设计器中调试自定义组件。
|
|
74
77
|
- **neo build2lib**: 产出 UMD/ESM 库文件(可配置)。
|
|
75
|
-
- **neo publish2oss**:
|
|
78
|
+
- **neo publish2oss**: 构建并上传到对象存储(可自定义配置对象存储)。
|
|
79
|
+
|
|
80
|
+
## 内置自动识别自定义组件
|
|
81
|
+
- **自动生成入口配置**: 当 `entry` 未配置时,会自动从 `src/components` 扫描并识别自定义组件,`src/components` 下的子目录名称作为自定义组件的 cmpType,并以其目录下的 `.ts/.tsx/.js/.jsx` 结尾的文件作为组件内容文件,model.[tj]s 作为模型内容文件;
|
|
82
|
+
- **自动生成相关注册文件并注入**: 自动生成自定义组件注册文件和模型注册文件,并注入到构建脚本中,无需用户关注 [neo-register](https://www.npmjs.com/package/neo-register)。
|
|
83
|
+
|
|
84
|
+
## 发布到对象存储(OSS)
|
|
85
|
+
执行 `npm run publish2oss` 即可构建并上传到对象存储。发布前请确保:
|
|
86
|
+
- **package.json 的 name 唯一**
|
|
87
|
+
- **version 不重复**
|
|
88
|
+
- 已按需配置对象存储参数(支持自定义)
|
|
89
|
+
|
|
90
|
+
支持发布指定自定义组件:
|
|
91
|
+
执行 `npm run publish2oss --cmpType=xxCmp`
|
|
76
92
|
|
|
77
93
|
## 配置说明(neo.config.js)
|
|
78
94
|
neo-cmp-cli 默认提供完整配置;如需自定义,使用 `neo config init` 生成 `neo.config.js` 并按需修改。
|
|
@@ -222,15 +238,5 @@ module.exports = {
|
|
|
222
238
|
}
|
|
223
239
|
```
|
|
224
240
|
|
|
225
|
-
## 多页面与模板
|
|
226
|
-
- **多页面入口**: 当 `entry` 仅配置一个且对应文件不存在时,会自动从 `src/pages` 扫描以 `.ts/.tsx/.js/.jsx` 结尾的文件作为入口,匹配同名 HTML 作为模板。
|
|
227
|
-
- **模板选择**: 优先使用 `./src/index.html`;不存在时使用内置默认模板。多页面时若存在同名 HTML,将其作为页面模板。
|
|
228
|
-
|
|
229
|
-
## 发布到对象存储(OSS)
|
|
230
|
-
执行 `npm run publish2oss` 即可构建并上传到对象存储。发布前请确保:
|
|
231
|
-
- **package.json 的 name 唯一**
|
|
232
|
-
- **version 不重复**
|
|
233
|
-
- 已按需配置对象存储参数(支持自定义)
|
|
234
|
-
|
|
235
241
|
---
|
|
236
242
|
如需更多细节与高级用法,请参考模板项目与源码注释。
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ const { resolveToCurrentRoot } = require('../utils/pathUtils');
|
|
|
8
8
|
* @param {*} defaultComponentsDir 默认组件目录
|
|
9
9
|
* @returns 组件入口文件
|
|
10
10
|
*/
|
|
11
|
-
module.exports = (defaultComponentsDir = './src/components') => {
|
|
11
|
+
module.exports = (defaultComponentsDir = './src/components', cmpType) => {
|
|
12
12
|
const widgetEntries = {};
|
|
13
13
|
const linkDebugEntries = {
|
|
14
14
|
index: []
|
|
@@ -24,7 +24,13 @@ module.exports = (defaultComponentsDir = './src/components') => {
|
|
|
24
24
|
|
|
25
25
|
try {
|
|
26
26
|
// 读取指定目录下的所有文件
|
|
27
|
-
|
|
27
|
+
let widgetDirs = [];
|
|
28
|
+
if (cmpType) {
|
|
29
|
+
// 如果传入了 cmpType,则只读取指定组件目录
|
|
30
|
+
widgetDirs = [cmpType];
|
|
31
|
+
} else {
|
|
32
|
+
widgetDirs = fs.readdirSync(componentsBaseDir);
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
// 遍历所有目录
|
|
30
36
|
widgetDirs.forEach((dir) => {
|
|
@@ -9,7 +9,7 @@ const getCmpModelRegister = require('./getCmpModelRegister');
|
|
|
9
9
|
* @param {*} defaultComponentsDir 默认组件目录
|
|
10
10
|
* @returns 组件入口文件
|
|
11
11
|
*/
|
|
12
|
-
module.exports = (defaultComponentsDir = './src/components') => {
|
|
12
|
+
module.exports = (defaultComponentsDir = './src/components', cmpType) => {
|
|
13
13
|
const widgetEntries = {};
|
|
14
14
|
const linkDebugEntries = {
|
|
15
15
|
index: []
|
|
@@ -31,7 +31,13 @@ module.exports = (defaultComponentsDir = './src/components') => {
|
|
|
31
31
|
|
|
32
32
|
try {
|
|
33
33
|
// 读取指定目录下的所有文件
|
|
34
|
-
|
|
34
|
+
let widgetDirs = [];
|
|
35
|
+
if (cmpType) {
|
|
36
|
+
// 如果传入了 cmpType,则只读取指定组件目录
|
|
37
|
+
widgetDirs = [cmpType];
|
|
38
|
+
} else {
|
|
39
|
+
widgetDirs = fs.readdirSync(componentsBaseDir);
|
|
40
|
+
}
|
|
35
41
|
|
|
36
42
|
// 遍历所有目录
|
|
37
43
|
widgetDirs.forEach((dir) => {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const { resolveToCurrentRoot } = require('../utils/pathUtils');
|
|
4
|
+
/**
|
|
5
|
+
* 更新发布日志
|
|
6
|
+
*/
|
|
7
|
+
module.exports = (curHistoryData) => {
|
|
8
|
+
// 创建存放 cli 的临时目录
|
|
9
|
+
const cliTempDir = resolveToCurrentRoot('./.neo-cli');
|
|
10
|
+
if (!fs.existsSync(cliTempDir)) {
|
|
11
|
+
fs.mkdirSync(cliTempDir);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const historyFile = `${cliTempDir}/history.json`;
|
|
15
|
+
let historyData = {};
|
|
16
|
+
if (fs.existsSync(historyFile)) {
|
|
17
|
+
// 读取历史文件
|
|
18
|
+
try {
|
|
19
|
+
historyData = JSON.parse(fs.readFileSync(historyFile, 'utf8'));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error('读取历史文件失败:', error);
|
|
22
|
+
historyData = {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const historyKey = new Date().toString();
|
|
26
|
+
historyData[historyKey] = curHistoryData;
|
|
27
|
+
|
|
28
|
+
// 更新历史文件
|
|
29
|
+
fs.writeFileSync(historyFile, JSON.stringify(historyData, null, 2));
|
|
30
|
+
};
|
package/src/module/index.js
CHANGED
|
@@ -232,16 +232,20 @@ yargs
|
|
|
232
232
|
}
|
|
233
233
|
)
|
|
234
234
|
.command(
|
|
235
|
-
'publish2oss',
|
|
235
|
+
'publish2oss [options]',
|
|
236
236
|
'发布到oss',
|
|
237
237
|
(yargs) => {
|
|
238
238
|
yargs
|
|
239
239
|
.reset()
|
|
240
|
-
.usage(titleTip('Usage') + ': $0
|
|
240
|
+
.usage(titleTip('Usage') + ': $0 publish2oss [options]')
|
|
241
|
+
.option('cmpType', {
|
|
242
|
+
alias: 't',
|
|
243
|
+
describe: '自定义组件名称'
|
|
244
|
+
})
|
|
241
245
|
.alias('h', 'help');
|
|
242
246
|
},
|
|
243
247
|
(argv) => {
|
|
244
|
-
mainAction.publish2oss(); // 构建并发布脚本到oss
|
|
248
|
+
mainAction.publish2oss(argv.cmpType); // 构建并发布脚本到oss
|
|
245
249
|
}
|
|
246
250
|
)
|
|
247
251
|
.command(
|
package/src/module/main.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const akfun = require('akfun');
|
|
2
|
-
const path = require('path');
|
|
3
2
|
const _ = require('lodash');
|
|
4
3
|
const neoInit = require('./neoInit');
|
|
5
4
|
const neoInitByCopy = require('./neoInitByCopy');
|
|
@@ -180,7 +179,7 @@ module.exports = {
|
|
|
180
179
|
|
|
181
180
|
akfun.build('lib', curConfig, consoleTag);
|
|
182
181
|
}, // 构建脚本:生产环境
|
|
183
|
-
publish2oss: () => {
|
|
182
|
+
publish2oss: (cmpType) => {
|
|
184
183
|
// 将 publish2oss 相关配置设置给 build2lib
|
|
185
184
|
const publish2ossConfig = curConfig.publish2oss;
|
|
186
185
|
curConfig.build2lib = Object.assign(curConfig.build2lib, publish2ossConfig);
|
|
@@ -193,12 +192,12 @@ module.exports = {
|
|
|
193
192
|
let entries = {};
|
|
194
193
|
if (curConfig.build2lib.disableAutoRegister) {
|
|
195
194
|
// disableAutoRegister 为 true 时,仅自动生成入口文件(不自动注册)
|
|
196
|
-
const { widgetEntries, cmpTypes } = getEntries(curConfig.componentsDir);
|
|
195
|
+
const { widgetEntries, cmpTypes } = getEntries(curConfig.componentsDir, cmpType);
|
|
197
196
|
entries = widgetEntries;
|
|
198
197
|
curCmpTypes = cmpTypes;
|
|
199
198
|
} else {
|
|
200
199
|
// 自动生成入口文件(并自动创建对应的注册文件)
|
|
201
|
-
const { widgetEntries, cmpTypes } = getEntriesWithAutoRegister(curConfig.componentsDir);
|
|
200
|
+
const { widgetEntries, cmpTypes } = getEntriesWithAutoRegister(curConfig.componentsDir, cmpType);
|
|
202
201
|
entries = widgetEntries;
|
|
203
202
|
curCmpTypes = cmpTypes;
|
|
204
203
|
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* 注入 neoRequire 函数
|
|
3
3
|
* 备注:用于实现和 Neo 平台共享依赖
|
|
4
4
|
*/
|
|
5
|
-
(function(
|
|
5
|
+
(function(NeoCustomCmpFileFactory) {
|
|
6
6
|
if (!window.neoRequire) {
|
|
7
7
|
throw new Error('neoRequire 不存在,请在 NeoCRM 平台中加载此脚本。');
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
NeoCustomCmpFileFactory(window.neoRequire);
|
|
10
10
|
})(function(require) {
|
|
11
11
|
/**
|
|
12
12
|
* 这里放自定义组件相关内容代码
|
package/src/oss/publish2oss.js
CHANGED
|
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { catchCurPackageJson } = require('../utils/pathUtils');
|
|
5
5
|
const getConfigObj = require('../utils/getConfigObj');
|
|
6
|
+
const updatePublishLog = require('../cmpUtils/updatePublishLog');
|
|
6
7
|
|
|
7
8
|
// 获取当前项目的package文件
|
|
8
9
|
const currentPackageJsonDir = catchCurPackageJson();
|
|
@@ -94,6 +95,8 @@ const publish2oss = (ossType, ossConfig, assetsRoot, fileExtensions = ['.js', '.
|
|
|
94
95
|
const widgetFilesMap = getResultFilesByWidgetName(results);
|
|
95
96
|
if (widgetFilesMap) {
|
|
96
97
|
console.info('上传至 OSS 的文件信息:\n', widgetFilesMap);
|
|
98
|
+
// 更新发布日志
|
|
99
|
+
updatePublishLog(widgetFilesMap);
|
|
97
100
|
}
|
|
98
101
|
})
|
|
99
102
|
.catch((error) => {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { ConcatSource } = require('webpack-sources');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 注入 neoRequire 函数
|
|
5
|
+
* 备注:用于实现和 Neo 平台共享依赖
|
|
6
|
+
*/
|
|
7
|
+
class AddNeoRequirePlugin {
|
|
8
|
+
apply(compiler) {
|
|
9
|
+
compiler.hooks.compilation.tap('AddNeoRequirePlugin', (compilation) => {
|
|
10
|
+
compilation.hooks.processAssets.tap(
|
|
11
|
+
{
|
|
12
|
+
name: 'AddNeoRequirePlugin',
|
|
13
|
+
stage: -100
|
|
14
|
+
},
|
|
15
|
+
() => {
|
|
16
|
+
for (const chunk of compilation.chunks) {
|
|
17
|
+
if (!chunk.canBeInitial() || !chunk.rendered) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const file of chunk.files) {
|
|
22
|
+
if (/.css$/.test(file)) {
|
|
23
|
+
// 不处理 css 文件
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const Header = `
|
|
28
|
+
(function(cmpFileFactory) {
|
|
29
|
+
if (!window.neoRequire) {
|
|
30
|
+
throw new Error('neoRequire 不存在,请在 NeoCRM 平台中加载此脚本。');
|
|
31
|
+
}
|
|
32
|
+
cmpFileFactory(window.neoRequire);
|
|
33
|
+
})(function(require) {
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const Footer = `})`;
|
|
37
|
+
|
|
38
|
+
compilation.updateAsset(file, (oldFileContent) => new ConcatSource(Header, oldFileContent, Footer));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = AddNeoRequirePlugin;
|
|
@@ -5,6 +5,83 @@ const { ConcatSource } = require('webpack-sources');
|
|
|
5
5
|
* 备注:用于实现和 Neo 平台共享依赖
|
|
6
6
|
*/
|
|
7
7
|
class AddNeoRequirePlugin {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.options = {
|
|
10
|
+
// 是否启用详细日志
|
|
11
|
+
verbose: options.verbose || false,
|
|
12
|
+
// 是否跳过已注入的文件
|
|
13
|
+
skipInjected: options.skipInjected !== false,
|
|
14
|
+
// 需要跳过的文件扩展名
|
|
15
|
+
skipExtensions: options.skipExtensions || ['.css', '.map', '.txt', '.html'],
|
|
16
|
+
// 需要跳过的文件名模式
|
|
17
|
+
skipPatterns: options.skipPatterns || [/\.map$/, /\.LICENSE\.txt$/, /\.html$/],
|
|
18
|
+
...options
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// 记录已处理的文件,避免重复处理
|
|
22
|
+
this.processedFiles = new Set();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 检查文件是否应该被跳过
|
|
27
|
+
*/
|
|
28
|
+
shouldSkipFile(filename) {
|
|
29
|
+
// 检查扩展名
|
|
30
|
+
for (const ext of this.options.skipExtensions) {
|
|
31
|
+
if (filename.endsWith(ext)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 检查文件名模式
|
|
37
|
+
for (const pattern of this.options.skipPatterns) {
|
|
38
|
+
if (pattern.test(filename)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 检查文件是否已经被注入过
|
|
48
|
+
*/
|
|
49
|
+
isAlreadyInjected(content) {
|
|
50
|
+
if (!content || typeof content !== 'string') {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 检查是否包含注入的标识
|
|
55
|
+
return content.includes('NeoCustomCmpFileFactory(window.neoRequire)') &&
|
|
56
|
+
content.includes('if (!window.neoRequire)');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 验证文件内容是否有效
|
|
61
|
+
*/
|
|
62
|
+
isValidFile(content) {
|
|
63
|
+
if (!content || typeof content !== 'string') {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 检查文件是否为空或只包含空白字符
|
|
68
|
+
if (content.trim().length === 0) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 记录日志
|
|
77
|
+
*/
|
|
78
|
+
log(message, level = 'info') {
|
|
79
|
+
if (this.options.verbose || level === 'error') {
|
|
80
|
+
const prefix = `[AddNeoRequirePlugin]`;
|
|
81
|
+
console[level](`${prefix} ${message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
8
85
|
apply(compiler) {
|
|
9
86
|
compiler.hooks.compilation.tap('AddNeoRequirePlugin', (compilation) => {
|
|
10
87
|
compilation.hooks.processAssets.tap(
|
|
@@ -12,36 +89,94 @@ class AddNeoRequirePlugin {
|
|
|
12
89
|
name: 'AddNeoRequirePlugin',
|
|
13
90
|
stage: -100
|
|
14
91
|
},
|
|
15
|
-
() => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
92
|
+
(assets) => {
|
|
93
|
+
this.log('开始处理资源文件...');
|
|
94
|
+
|
|
95
|
+
let processedCount = 0;
|
|
96
|
+
let skippedCount = 0;
|
|
97
|
+
let errorCount = 0;
|
|
20
98
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
99
|
+
// 遍历所有资源文件
|
|
100
|
+
for (const [filename, asset] of Object.entries(assets)) {
|
|
101
|
+
try {
|
|
102
|
+
// 检查是否应该跳过此文件
|
|
103
|
+
if (this.shouldSkipFile(filename)) {
|
|
104
|
+
this.log(`跳过文件: ${filename} (匹配跳过规则)`);
|
|
105
|
+
skippedCount++;
|
|
24
106
|
continue;
|
|
25
107
|
}
|
|
26
108
|
|
|
27
|
-
|
|
28
|
-
(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})(function(require) {
|
|
34
|
-
`;
|
|
109
|
+
// 检查是否已经处理过
|
|
110
|
+
if (this.processedFiles.has(filename)) {
|
|
111
|
+
this.log(`跳过文件: ${filename} (已处理过)`);
|
|
112
|
+
skippedCount++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
35
115
|
|
|
36
|
-
|
|
116
|
+
// 获取文件内容
|
|
117
|
+
const source = asset.source();
|
|
118
|
+
const content = typeof source === 'string' ? source : source.toString();
|
|
37
119
|
|
|
38
|
-
|
|
120
|
+
// 验证文件内容
|
|
121
|
+
if (!this.isValidFile(content)) {
|
|
122
|
+
this.log(`跳过文件: ${filename} (内容无效)`, 'warn');
|
|
123
|
+
skippedCount++;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 检查是否已经注入过
|
|
128
|
+
if (this.options.skipInjected && this.isAlreadyInjected(content)) {
|
|
129
|
+
this.log(`跳过文件: ${filename} (已注入过)`);
|
|
130
|
+
skippedCount++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 执行注入
|
|
135
|
+
this.injectNeoRequire(compilation, filename, content);
|
|
136
|
+
this.processedFiles.add(filename);
|
|
137
|
+
processedCount++;
|
|
138
|
+
|
|
139
|
+
this.log(`成功处理文件: ${filename}`);
|
|
140
|
+
|
|
141
|
+
} catch (error) {
|
|
142
|
+
errorCount++;
|
|
143
|
+
this.log(`处理文件失败: ${filename}, 错误: ${error.message}`, 'error');
|
|
144
|
+
console.error(`[AddNeoRequirePlugin] 详细错误:`, error);
|
|
39
145
|
}
|
|
40
146
|
}
|
|
147
|
+
|
|
148
|
+
// 输出处理统计
|
|
149
|
+
this.log(`处理完成 - 成功: ${processedCount}, 跳过: ${skippedCount}, 错误: ${errorCount}`);
|
|
150
|
+
|
|
151
|
+
if (errorCount > 0) {
|
|
152
|
+
this.log(`警告: 有 ${errorCount} 个文件处理失败`, 'warn');
|
|
153
|
+
}
|
|
41
154
|
}
|
|
42
155
|
);
|
|
43
156
|
});
|
|
44
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 执行 neoRequire 注入
|
|
161
|
+
*/
|
|
162
|
+
injectNeoRequire(compilation, filename, content) {
|
|
163
|
+
const Header = `
|
|
164
|
+
(function(NeoCustomCmpFileFactory) {
|
|
165
|
+
if (!window.neoRequire) {
|
|
166
|
+
throw new Error('neoRequire 不存在,请在 NeoCRM 平台中加载此脚本。');
|
|
167
|
+
}
|
|
168
|
+
NeoCustomCmpFileFactory(window.neoRequire);
|
|
169
|
+
})(function(require) {
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
const Footer = `})`;
|
|
173
|
+
|
|
174
|
+
// 创建新的资源
|
|
175
|
+
const newSource = new ConcatSource(Header, content, Footer);
|
|
176
|
+
|
|
177
|
+
// 更新资源
|
|
178
|
+
compilation.updateAsset(filename, newSource);
|
|
179
|
+
}
|
|
45
180
|
}
|
|
46
181
|
|
|
47
182
|
module.exports = AddNeoRequirePlugin;
|
package/src/plugins/README.md
CHANGED
|
@@ -1,2 +1,109 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
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,11 +1,6 @@
|
|
|
1
1
|
# XObject 数据表格组件
|
|
2
2
|
基于 XObject 的数据表格组件,支持增删改查操作,表格列根据 `getXObjectDesc` 返回的 fields 动态生成。
|
|
3
3
|
|
|
4
|
-
- 对应的生成指令
|
|
5
|
-
```
|
|
6
|
-
@utils/ 请根据这里提供的方法,参照 @contact-card-list/ 使用 antd 组件 写一个 表格组件,展示属性配置 xobject 对应的 业务数据列表,支持创建、编辑、删除操作。其中表格展示的列 根据 getXObjectDesc 返回的 fields 生成,表格的 Title 根据 其返回的 label 显示。另外 请使用 propsSchemaCreator 生成自定义组件的属性配置,支持 xobject 选择配置,其中 xobject 的选项 使用 getEntityList获取。
|
|
7
|
-
```
|
|
8
|
-
|
|
9
4
|
## 功能特性
|
|
10
5
|
|
|
11
6
|
- 📊 **动态表格列**:根据 XObject 字段描述自动生成表格列
|
|
@@ -142,6 +142,14 @@ export default class XObjectTable extends React.PureComponent<
|
|
|
142
142
|
entityTypeList: [],
|
|
143
143
|
};
|
|
144
144
|
|
|
145
|
+
if (this.props.xObjectApiKey) {
|
|
146
|
+
// 初始化字段列表、加载数据和业务类型列表
|
|
147
|
+
this.loadFieldList().then(() => {
|
|
148
|
+
this.loadData();
|
|
149
|
+
});
|
|
150
|
+
this.getEntityTypeList();
|
|
151
|
+
}
|
|
152
|
+
|
|
145
153
|
// 绑定方法上下文
|
|
146
154
|
this.loadData = this.loadData.bind(this);
|
|
147
155
|
this.loadFieldList = this.loadFieldList.bind(this);
|
|
@@ -153,18 +161,6 @@ export default class XObjectTable extends React.PureComponent<
|
|
|
153
161
|
this.handleTableChange = this.handleTableChange.bind(this);
|
|
154
162
|
}
|
|
155
163
|
|
|
156
|
-
/**
|
|
157
|
-
* 组件挂载后执行
|
|
158
|
-
* 初始化字段列表、加载数据和业务类型列表
|
|
159
|
-
*/
|
|
160
|
-
async componentDidMount() {
|
|
161
|
-
if (this.props.xObjectApiKey) {
|
|
162
|
-
await this.loadFieldList();
|
|
163
|
-
this.loadData();
|
|
164
|
-
this.getEntityTypeList();
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
164
|
/**
|
|
169
165
|
* 获取业务类型列表
|
|
170
166
|
* 用于下拉选择框的选项数据
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
# XObject 数据表格组件
|
|
2
2
|
基于 XObject 的数据表格组件,支持增删改查操作,表格列根据 `getXObjectDesc` 返回的 fields 动态生成。
|
|
3
3
|
|
|
4
|
-
- 对应的生成指令
|
|
5
|
-
```
|
|
6
|
-
@utils/ 请根据这里提供的方法,参照 @contact-card-list/ 使用 antd 组件 写一个 表格组件,展示属性配置 xobject 对应的 业务数据列表,支持创建、编辑、删除操作。其中表格展示的列 根据 getXObjectDesc 返回的 fields 生成,表格的 Title 根据 其返回的 label 显示。另外 请使用 propsSchemaCreator 生成自定义组件的属性配置,支持 xobject 选择配置,其中 xobject 的选项 使用 getEntityList获取。
|
|
7
|
-
```
|
|
8
|
-
|
|
9
4
|
## 功能特性
|
|
10
5
|
|
|
11
6
|
- 📊 **动态表格列**:根据 XObject 字段描述自动生成表格列
|
|
@@ -136,6 +136,14 @@ export default class XObjectTable extends React.PureComponent<
|
|
|
136
136
|
entityTypeList: [],
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
+
if (this.props.xObjectApiKey) {
|
|
140
|
+
// 初始化字段列表、加载数据和业务类型列表
|
|
141
|
+
this.loadFieldList().then(() => {
|
|
142
|
+
this.loadData();
|
|
143
|
+
});
|
|
144
|
+
this.getEntityTypeList();
|
|
145
|
+
}
|
|
146
|
+
|
|
139
147
|
// 绑定方法上下文
|
|
140
148
|
this.loadData = this.loadData.bind(this);
|
|
141
149
|
this.loadFieldList = this.loadFieldList.bind(this);
|
|
@@ -147,18 +155,6 @@ export default class XObjectTable extends React.PureComponent<
|
|
|
147
155
|
this.handleTableChange = this.handleTableChange.bind(this);
|
|
148
156
|
}
|
|
149
157
|
|
|
150
|
-
/**
|
|
151
|
-
* 组件挂载后执行
|
|
152
|
-
* 初始化字段列表、加载数据和业务类型列表
|
|
153
|
-
*/
|
|
154
|
-
async componentDidMount() {
|
|
155
|
-
if (this.props.xObjectApiKey) {
|
|
156
|
-
await this.loadFieldList();
|
|
157
|
-
this.loadData();
|
|
158
|
-
await this.getEntityTypeList();
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
158
|
/**
|
|
163
159
|
* 获取业务类型列表
|
|
164
160
|
* 用于下拉选择框的选项数据
|