@kdcloudjs/cli 0.0.2 → 0.0.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.
- package/package.json +2 -1
- package/src/actions/debug/index.js +8 -5
- package/src/actions/debug/runner.js +50 -5
- package/src/actions/env/info.js +3 -1
- package/src/actions/env/list.js +3 -1
- package/src/actions/project/create/createMetaXml.js +37 -6
- package/src/actions/project/create/page.js +11 -0
- package/src/actions/project/create/validate.js +2 -2
- package/src/actions/project/deploy/upload.js +9 -3
- package/src/actions/project/init/index.js +5 -5
- package/src/api/index.js +10 -10
- package/src/api/uploader/updateKwcMeta.js +3 -3
- package/src/api/uploader/updatePageMeta.js +3 -3
- package/src/commands/env/index.js +11 -1
- package/src/commands/project/index.js +5 -0
- package/src/commands/update.js +52 -0
- package/src/index.js +7 -2
- package/src/utils/help.js +18 -0
- package/src/utils/printTable.js +94 -24
- package/src/utils/projectConfig.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kdcloudjs/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Kingdee CLI",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"axios": "^1.12.2",
|
|
19
19
|
"chalk": "4.1.2",
|
|
20
|
+
"cli-table3": "^0.6.5",
|
|
20
21
|
"commander": "12.1.0",
|
|
21
22
|
"download-git-repo": "^3.0.2",
|
|
22
23
|
"form-data": "^4.0.4",
|
|
@@ -87,16 +87,19 @@ module.exports = async function debug(options = {}) {
|
|
|
87
87
|
|
|
88
88
|
// 6. Open Browser & Start Debug
|
|
89
89
|
// Start server first
|
|
90
|
-
const {
|
|
90
|
+
const { serverPromise, serverChild, buildPromise, buildReadyPromise } = runDebug(root)
|
|
91
91
|
|
|
92
92
|
try {
|
|
93
|
-
info('Waiting for debug server
|
|
94
|
-
await
|
|
93
|
+
info('Waiting for debug server and build watcher...')
|
|
94
|
+
await Promise.all([
|
|
95
|
+
waitForPort(3333, serverChild),
|
|
96
|
+
buildReadyPromise
|
|
97
|
+
])
|
|
95
98
|
info(`Opening: ${finalUrl}`)
|
|
96
99
|
openBrowser(finalUrl)
|
|
97
100
|
} catch (e) {
|
|
98
|
-
error(`
|
|
101
|
+
error(`Start failed: ${e.message}`)
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
await serverPromise
|
|
104
|
+
await Promise.all([serverPromise, buildPromise])
|
|
102
105
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
1
3
|
const { spawn } = require('child_process')
|
|
2
4
|
const { info } = require('../../utils/log')
|
|
3
5
|
|
|
@@ -17,19 +19,62 @@ function installDeps(root) {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
function runDebug(root) {
|
|
20
|
-
info('Starting
|
|
21
|
-
let
|
|
22
|
-
|
|
22
|
+
info('Starting build watcher...')
|
|
23
|
+
let buildChild
|
|
24
|
+
let buildReadyResolve
|
|
25
|
+
const buildReadyPromise = new Promise(resolve => {
|
|
26
|
+
buildReadyResolve = resolve
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// Poll dist/kwc for content
|
|
30
|
+
const checkInterval = setInterval(() => {
|
|
31
|
+
const distDir = path.join(root, 'dist/kwc')
|
|
32
|
+
if (fs.existsSync(distDir)) {
|
|
33
|
+
try {
|
|
34
|
+
const files = fs.readdirSync(distDir)
|
|
35
|
+
if (files.length > 0) {
|
|
36
|
+
clearInterval(checkInterval)
|
|
37
|
+
buildReadyResolve()
|
|
38
|
+
}
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// ignore
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}, 1000)
|
|
44
|
+
|
|
45
|
+
const buildPromise = new Promise((resolve, reject) => {
|
|
23
46
|
const child = spawn('npm', ['run', 'debug'], {
|
|
24
47
|
cwd: root,
|
|
25
48
|
stdio: 'inherit',
|
|
26
49
|
shell: true
|
|
27
50
|
})
|
|
28
|
-
|
|
51
|
+
buildChild = child
|
|
52
|
+
|
|
53
|
+
child.on('close', (code) => {
|
|
54
|
+
clearInterval(checkInterval)
|
|
55
|
+
buildReadyResolve()
|
|
56
|
+
resolve(code)
|
|
57
|
+
})
|
|
58
|
+
child.on('error', (err) => {
|
|
59
|
+
clearInterval(checkInterval)
|
|
60
|
+
buildReadyResolve()
|
|
61
|
+
reject(err)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
info('Starting debug server...')
|
|
66
|
+
let serverChild
|
|
67
|
+
const serverPromise = new Promise((resolve, reject) => {
|
|
68
|
+
const child = spawn('npm', ['run', 'server'], {
|
|
69
|
+
cwd: root,
|
|
70
|
+
stdio: 'inherit',
|
|
71
|
+
shell: true
|
|
72
|
+
})
|
|
73
|
+
serverChild = child
|
|
29
74
|
child.on('close', resolve)
|
|
30
75
|
child.on('error', reject)
|
|
31
76
|
})
|
|
32
|
-
return {
|
|
77
|
+
return { serverPromise, serverChild, buildPromise, buildChild, buildReadyPromise }
|
|
33
78
|
}
|
|
34
79
|
|
|
35
80
|
module.exports = {
|
package/src/actions/env/info.js
CHANGED
package/src/actions/env/list.js
CHANGED
|
@@ -10,16 +10,47 @@ module.exports = function createMetaXml({ name, moduleId, framework }) {
|
|
|
10
10
|
<moduleid>${moduleId}</moduleid>
|
|
11
11
|
<framework>${framework}</framework>
|
|
12
12
|
<targets>
|
|
13
|
-
<target>BaseFormModel</target>
|
|
14
|
-
<target>BillFormModel</target>
|
|
15
|
-
<target>DynamicFormModel</target>
|
|
16
|
-
<target>MobileBillFormModel</target>
|
|
17
|
-
<target>MobileFormModel</target>
|
|
18
13
|
<target>KWCFormModel</target>
|
|
19
14
|
</targets>
|
|
20
15
|
<targetConfigs>
|
|
21
16
|
<targetConfig>
|
|
22
|
-
<targets>
|
|
17
|
+
<targets>KWCFormModel</targets>
|
|
18
|
+
<!-- <property
|
|
19
|
+
description="文本类型属性"
|
|
20
|
+
name="StringValue"
|
|
21
|
+
caption="文本类型属性"
|
|
22
|
+
type="String"
|
|
23
|
+
length="25"
|
|
24
|
+
default="默认值">
|
|
25
|
+
</property>
|
|
26
|
+
<property
|
|
27
|
+
name="IntValue"
|
|
28
|
+
type="Integer"
|
|
29
|
+
caption="数值类型属性"
|
|
30
|
+
description="数值类型属性"
|
|
31
|
+
max="10"
|
|
32
|
+
min="2"
|
|
33
|
+
default="2"
|
|
34
|
+
></property>
|
|
35
|
+
<property
|
|
36
|
+
name="ComboValue"
|
|
37
|
+
type="Combo"
|
|
38
|
+
default="0"
|
|
39
|
+
caption="下拉列表类型属性"
|
|
40
|
+
description="下拉列表类型属性" >
|
|
41
|
+
<items>
|
|
42
|
+
<item id="0" name="默认名称" />
|
|
43
|
+
<item id="1" name="选项1" />
|
|
44
|
+
<item id="2" name="选项2" />
|
|
45
|
+
</items>
|
|
46
|
+
</property>
|
|
47
|
+
|
|
48
|
+
<property name="BooleanValue"
|
|
49
|
+
type="Boolean"
|
|
50
|
+
caption="布尔类型属性"
|
|
51
|
+
description="布尔类型属性"
|
|
52
|
+
default="true" >
|
|
53
|
+
</property> -->
|
|
23
54
|
</targetConfig>
|
|
24
55
|
</targetConfigs>
|
|
25
56
|
</KingdeeComponentBundle>`
|
|
@@ -35,6 +35,17 @@ function createPageXml(name) {
|
|
|
35
35
|
<region>
|
|
36
36
|
<name>region1</name>
|
|
37
37
|
<controls>
|
|
38
|
+
<!-- <control>
|
|
39
|
+
<type>ReactDemoAp</type>
|
|
40
|
+
<name>ReactDemo</name>
|
|
41
|
+
<label>测试组件名称</label>
|
|
42
|
+
<propertys>
|
|
43
|
+
<property>
|
|
44
|
+
<name>StringValue</name>
|
|
45
|
+
<value>3333</value>
|
|
46
|
+
</property>
|
|
47
|
+
</propertys>
|
|
48
|
+
</control> -->
|
|
38
49
|
</controls>
|
|
39
50
|
</region>
|
|
40
51
|
</regions>
|
|
@@ -27,8 +27,8 @@ module.exports = function validate({ name, type, framework }) {
|
|
|
27
27
|
}
|
|
28
28
|
break
|
|
29
29
|
case FRAMEWORKS.React:
|
|
30
|
-
if (!/^[A-Z][a-zA-Z0-9]*$/.test(name)
|
|
31
|
-
error('React component name must follow PascalCase (start with uppercase)
|
|
30
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(name)) {
|
|
31
|
+
error('React component name must follow PascalCase (start with uppercase)')
|
|
32
32
|
process.exit(1)
|
|
33
33
|
}
|
|
34
34
|
break
|
|
@@ -50,11 +50,17 @@ exports.upload = async function upload(files, env) {
|
|
|
50
50
|
const headers = ['Type', 'File', 'Status', 'Message']
|
|
51
51
|
const rows = results.map(file => [
|
|
52
52
|
file.type,
|
|
53
|
-
file.path,
|
|
53
|
+
path.relative(process.cwd(), file.path),
|
|
54
54
|
file.status === 'success' ? '✔' : '✖',
|
|
55
|
-
file.status === 'failed' ?
|
|
55
|
+
file.status === 'failed' ? `${file.message}` : 'Success'
|
|
56
56
|
])
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
const hasFailed = results.some(r => r.status === 'failed')
|
|
59
|
+
const colWidths = hasFailed ? [10, 30, 10, 50] : [10, 50, 10, 30]
|
|
60
|
+
|
|
61
|
+
printTable(headers, rows, {
|
|
62
|
+
colWidths
|
|
63
|
+
})
|
|
58
64
|
console.timeEnd(`🕒 Upload duration`)
|
|
59
65
|
return results
|
|
60
66
|
}
|
|
@@ -8,15 +8,15 @@ const { TEMPLATES, INNER_TEMPLATES } = require('../constants')
|
|
|
8
8
|
|
|
9
9
|
module.exports = async function initProject({ projectName: _projectName, source }) {
|
|
10
10
|
try {
|
|
11
|
+
if (fs.existsSync(path.resolve(process.cwd(), _projectName))) {
|
|
12
|
+
error(`Directory already exists: ${_projectName}`)
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
const { projectName, moduleId, framework, language } = await collectInitInfo(_projectName) || {}
|
|
12
17
|
|
|
13
18
|
const targetDir = path.resolve(process.cwd(), projectName)
|
|
14
19
|
|
|
15
|
-
if (fs.existsSync(targetDir)) {
|
|
16
|
-
error(`Directory already exists: ${projectName}`)
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
20
|
tip(`\n🚀 Creating project: ${projectName}\n`)
|
|
21
21
|
console.time('🕒 Project initialization')
|
|
22
22
|
|
package/src/api/index.js
CHANGED
|
@@ -18,17 +18,17 @@ async function getAllDataCenters (url) {
|
|
|
18
18
|
value: d.accountId
|
|
19
19
|
})
|
|
20
20
|
} else {
|
|
21
|
-
error(`⚠
|
|
21
|
+
error(`⚠ Data center response error, please check if url: ${url} is correct`)
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
} else {
|
|
25
|
-
error(`⚠
|
|
25
|
+
error(`⚠ Data center list is empty, please check if url: ${url} is correct`)
|
|
26
26
|
}
|
|
27
27
|
} else {
|
|
28
|
-
error(`⚠
|
|
28
|
+
error(`⚠ Request failed, please check if url: ${url} is correct. error message: ${resp?.statusText}`)
|
|
29
29
|
}
|
|
30
30
|
} catch (e) {
|
|
31
|
-
error(`⚠
|
|
31
|
+
error(`⚠ Request failed, please check if url: ${url} is correct. error message: ${e?.message}`)
|
|
32
32
|
}
|
|
33
33
|
return res
|
|
34
34
|
}
|
|
@@ -58,13 +58,13 @@ async function getAccessToken (url, params) {
|
|
|
58
58
|
if (respData?.status) {
|
|
59
59
|
res = respData?.data || {}
|
|
60
60
|
} else {
|
|
61
|
-
error(`⚠
|
|
61
|
+
error(`⚠ Failed to get access_token, please check url, client_id, client_secret, username. error message: ${respData?.message}`)
|
|
62
62
|
}
|
|
63
63
|
} else {
|
|
64
|
-
error(`⚠
|
|
64
|
+
error(`⚠ Failed to get access_token, please check url, client_id, client_secret, username. response status: ${resp?.status}`)
|
|
65
65
|
}
|
|
66
66
|
} catch (e) {
|
|
67
|
-
error(`⚠
|
|
67
|
+
error(`⚠ Failed to get access_token, please check url, client_id, client_secret, username. error message: ${e?.message}`)
|
|
68
68
|
}
|
|
69
69
|
return res
|
|
70
70
|
}
|
|
@@ -85,13 +85,13 @@ async function getIsv (url, access_token) {
|
|
|
85
85
|
if (respData?.status) {
|
|
86
86
|
res = respData?.data || {}
|
|
87
87
|
} else {
|
|
88
|
-
error(`⚠ get isv fail
|
|
88
|
+
error(`⚠ get isv fail, pls check url and credentials info, errormessage: ${respData?.message}`)
|
|
89
89
|
}
|
|
90
90
|
} else {
|
|
91
|
-
error(`⚠ get isv fail
|
|
91
|
+
error(`⚠ get isv fail, pls check url and credentials info, response status: ${resp?.status}`)
|
|
92
92
|
}
|
|
93
93
|
} catch (err) {
|
|
94
|
-
error(`⚠ getIsv fail
|
|
94
|
+
error(`⚠ getIsv fail. error message: ${err.message}`)
|
|
95
95
|
}
|
|
96
96
|
return res
|
|
97
97
|
}
|
|
@@ -9,7 +9,7 @@ const { decrypt } = require('../../utils/crypto')
|
|
|
9
9
|
module.exports.updateKwcMeta = async function updateKwcMeta(filePath, env = {}) {
|
|
10
10
|
// TODO: 调用真实接口
|
|
11
11
|
if (!fs.existsSync(filePath)) {
|
|
12
|
-
throw new Error(`❌
|
|
12
|
+
throw new Error(`❌ File not found: ${filePath}`)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const { url: backendUrl, client_id, client_secret, username, accountId, access_token } = env
|
|
@@ -62,7 +62,7 @@ module.exports.updateKwcMeta = async function updateKwcMeta(filePath, env = {})
|
|
|
62
62
|
return res
|
|
63
63
|
}
|
|
64
64
|
} catch (err) {
|
|
65
|
-
console.warn(`⚠
|
|
65
|
+
console.warn(`⚠ Text upload failed, trying file upload: ${err.message}`)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// ============ 第二阶段:FormData 文件流上传 ============
|
|
@@ -85,6 +85,6 @@ module.exports.updateKwcMeta = async function updateKwcMeta(filePath, env = {})
|
|
|
85
85
|
}
|
|
86
86
|
throw new Error(`File upload failed: ${res?.data?.message || res.status}`)
|
|
87
87
|
} catch (err) {
|
|
88
|
-
throw new Error(
|
|
88
|
+
throw new Error(`${err.message}`)
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -14,7 +14,7 @@ const { decrypt } = require('../../utils/crypto')
|
|
|
14
14
|
module.exports.updatePageMeta = async function updatePageMeta(filePath, env = {}) {
|
|
15
15
|
// TODO: 调用真实接口
|
|
16
16
|
if (!fs.existsSync(filePath)) {
|
|
17
|
-
throw new Error(`❌
|
|
17
|
+
throw new Error(`❌ File not found: ${filePath}`)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const { url: backendUrl, client_id, client_secret, username, accountId, access_token } = env
|
|
@@ -66,7 +66,7 @@ module.exports.updatePageMeta = async function updatePageMeta(filePath, env = {}
|
|
|
66
66
|
return res
|
|
67
67
|
}
|
|
68
68
|
} catch (err) {
|
|
69
|
-
console.warn(`⚠
|
|
69
|
+
console.warn(`⚠ Text upload failed, trying file upload: ${err.message}`)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// ============ 第二阶段:FormData 文件流上传 ============
|
|
@@ -89,6 +89,6 @@ module.exports.updatePageMeta = async function updatePageMeta(filePath, env = {}
|
|
|
89
89
|
}
|
|
90
90
|
throw new Error(`File upload failed: ${res?.data?.message || res.status}`)
|
|
91
91
|
} catch (err) {
|
|
92
|
-
throw new Error(
|
|
92
|
+
throw new Error(`${err.message}`)
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
const KdHelp = require('../../utils/help')
|
|
2
|
+
|
|
1
3
|
module.exports = function registerEnv(program) {
|
|
2
4
|
const env = program
|
|
3
5
|
.command('env')
|
|
4
6
|
.description('Environment management')
|
|
7
|
+
.usage('[command] [options]')
|
|
8
|
+
|
|
9
|
+
env.createHelp = () => new KdHelp(env.configureHelp())
|
|
5
10
|
|
|
6
11
|
env
|
|
7
12
|
.command('create <name>')
|
|
@@ -11,9 +16,14 @@ module.exports = function registerEnv(program) {
|
|
|
11
16
|
require('./create')(name, options)
|
|
12
17
|
)
|
|
13
18
|
|
|
14
|
-
env
|
|
19
|
+
const set = env
|
|
15
20
|
.command('set')
|
|
16
21
|
.description('Set environment config')
|
|
22
|
+
.usage('[command] [options]')
|
|
23
|
+
|
|
24
|
+
set.createHelp = () => new KdHelp(set.configureHelp())
|
|
25
|
+
|
|
26
|
+
set
|
|
17
27
|
.command('target-env <name>')
|
|
18
28
|
.description('Set default environment')
|
|
19
29
|
.action(name =>
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
const KdHelp = require('../../utils/help')
|
|
2
|
+
|
|
1
3
|
module.exports = function registerProject(program) {
|
|
2
4
|
const project = program
|
|
3
5
|
.command('project')
|
|
4
6
|
.description('Project management')
|
|
7
|
+
.usage('[command] [options]')
|
|
8
|
+
|
|
9
|
+
project.createHelp = () => new KdHelp(project.configureHelp())
|
|
5
10
|
|
|
6
11
|
project
|
|
7
12
|
.command('init <name>')
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const { execSync } = require('child_process')
|
|
2
|
+
const os = require('os')
|
|
3
|
+
const ora = require('ora')
|
|
4
|
+
const chalk = require('chalk')
|
|
5
|
+
const { info, success, error } = require('../utils/log')
|
|
6
|
+
const pkg = require('../../package.json')
|
|
7
|
+
|
|
8
|
+
module.exports = async () => {
|
|
9
|
+
const spinner = ora('Checking for updates...').start()
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Check latest version
|
|
13
|
+
let latestVersion
|
|
14
|
+
try {
|
|
15
|
+
latestVersion = execSync(`npm view ${pkg.name} version`, { encoding: 'utf8', cwd: os.homedir() }).trim()
|
|
16
|
+
} catch (e) {
|
|
17
|
+
spinner.stop()
|
|
18
|
+
error('Failed to check for updates. Please check your network connection.')
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (latestVersion === pkg.version) {
|
|
23
|
+
spinner.stop()
|
|
24
|
+
success(`You are already using the latest version (${chalk.green(pkg.version)}).`)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
spinner.stop()
|
|
29
|
+
info(`New version available: ${chalk.green(latestVersion)} (current: ${chalk.yellow(pkg.version)})`)
|
|
30
|
+
spinner.start('Updating...')
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Execute update
|
|
34
|
+
// Use os.homedir() as cwd to avoid reading project-specific .npmrc which might cause warnings
|
|
35
|
+
execSync(`npm install -g ${pkg.name}@latest`, { stdio: 'inherit', cwd: os.homedir() })
|
|
36
|
+
spinner.stop()
|
|
37
|
+
success(`Successfully updated to version ${chalk.green(latestVersion)}`)
|
|
38
|
+
} catch (err) {
|
|
39
|
+
spinner.stop()
|
|
40
|
+
error('Update failed')
|
|
41
|
+
console.log(chalk.red('\nPlease try running the following command manually:'))
|
|
42
|
+
console.log(chalk.cyan(`npm install -g ${pkg.name}@latest`))
|
|
43
|
+
if (process.platform !== 'win32') {
|
|
44
|
+
console.log(chalk.dim('(You may need to use sudo)'))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
spinner.stop()
|
|
49
|
+
error('An error occurred during update')
|
|
50
|
+
console.error(error)
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.js
CHANGED
|
@@ -3,15 +3,20 @@
|
|
|
3
3
|
const { Command } = require('commander')
|
|
4
4
|
const pkg = require('../package.json')
|
|
5
5
|
const checkUpdate = require('./utils/checkUpdate')
|
|
6
|
+
const KdHelp = require('./utils/help')
|
|
6
7
|
|
|
7
8
|
const program = new Command()
|
|
9
|
+
program.createHelp = () => new KdHelp(program.configureHelp())
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
if (!process.argv.includes('update')) {
|
|
12
|
+
checkUpdate()
|
|
13
|
+
}
|
|
10
14
|
|
|
11
15
|
program
|
|
12
16
|
.name('kd')
|
|
17
|
+
.usage('[command] [options]')
|
|
13
18
|
.description('Kingdee CLI')
|
|
14
|
-
.version(pkg.version)
|
|
19
|
+
.version(pkg.version, '-v, --version')
|
|
15
20
|
|
|
16
21
|
program
|
|
17
22
|
.command('update')
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { Help } = require('commander')
|
|
2
|
+
|
|
3
|
+
class KdHelp extends Help {
|
|
4
|
+
subcommandTerm(cmd) {
|
|
5
|
+
if (cmd.name() === 'init') {
|
|
6
|
+
return 'init <name> [options]'
|
|
7
|
+
}
|
|
8
|
+
if (cmd.name() === 'create') {
|
|
9
|
+
return 'create <name> [options]'
|
|
10
|
+
}
|
|
11
|
+
if (cmd.name() === 'target-env') {
|
|
12
|
+
return 'target-env <name> [options]'
|
|
13
|
+
}
|
|
14
|
+
return super.subcommandTerm(cmd)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = KdHelp
|
package/src/utils/printTable.js
CHANGED
|
@@ -1,28 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const Table = require('cli-table3')
|
|
2
|
+
|
|
3
|
+
let stringWidth
|
|
4
|
+
try {
|
|
5
|
+
const m = require('string-width')
|
|
6
|
+
stringWidth = m.default || m
|
|
7
|
+
} catch (e) {
|
|
8
|
+
// Fallback (simple approximation) if string-width is not available
|
|
9
|
+
stringWidth = (str) => {
|
|
10
|
+
let width = 0
|
|
11
|
+
for (let i = 0; i < str.length; i++) {
|
|
12
|
+
width += str.charCodeAt(i) > 255 ? 2 : 1
|
|
13
|
+
}
|
|
14
|
+
return width
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function wrapText(text, width) {
|
|
19
|
+
if (width <= 0) return text
|
|
20
|
+
const lines = []
|
|
21
|
+
let line = ''
|
|
22
|
+
let lineWidth = 0
|
|
23
|
+
|
|
24
|
+
for (const char of text) {
|
|
25
|
+
const cw = stringWidth(char)
|
|
26
|
+
if (lineWidth + cw > width) {
|
|
27
|
+
lines.push(line)
|
|
28
|
+
line = char
|
|
29
|
+
lineWidth = cw
|
|
30
|
+
} else {
|
|
31
|
+
line += char
|
|
32
|
+
lineWidth += cw
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
lines.push(line)
|
|
36
|
+
return lines.join('\n')
|
|
4
37
|
}
|
|
5
38
|
|
|
6
|
-
module.exports = function printTable(headers, rows) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
module.exports = function printTable(headers, rows, options = {}) {
|
|
40
|
+
const MAX_LENGTH = 500
|
|
41
|
+
|
|
42
|
+
// Pre-process rows to handle flattening, truncation, and manual wrapping
|
|
43
|
+
const processedRows = rows.map(row => {
|
|
44
|
+
return row.map((cell, colIndex) => {
|
|
45
|
+
if (typeof cell !== 'string') return cell
|
|
46
|
+
|
|
47
|
+
let text = cell
|
|
48
|
+
|
|
49
|
+
// 1. Truncate: Limit total length first to avoid processing huge strings
|
|
50
|
+
if (text.length > MAX_LENGTH) {
|
|
51
|
+
text = text.slice(0, MAX_LENGTH) + '... (truncated)'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 2. Manual Wrap: If colWidth is provided, wrap manually to avoid cli-table3 CJK bugs
|
|
55
|
+
// We must handle existing newlines by wrapping each line individually
|
|
56
|
+
const colWidth = options.colWidths ? options.colWidths[colIndex] : null
|
|
57
|
+
if (colWidth && typeof colWidth === 'number') {
|
|
58
|
+
// cli-table3 default padding is 1 left + 1 right = 2
|
|
59
|
+
// We use a larger safety margin (3 or 4) to ensure we NEVER exceed the width
|
|
60
|
+
// due to potential width calculation mismatches between string-width versions
|
|
61
|
+
const padding = 2
|
|
62
|
+
const safetyMargin = 2
|
|
63
|
+
const contentWidth = colWidth - padding - safetyMargin
|
|
64
|
+
|
|
65
|
+
if (contentWidth > 0) {
|
|
66
|
+
// Split by existing newlines, wrap each segment, then rejoin
|
|
67
|
+
text = text.split('\n').map(line => wrapText(line, contentWidth)).join('\n')
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return text
|
|
72
|
+
})
|
|
27
73
|
})
|
|
74
|
+
|
|
75
|
+
const table = new Table({
|
|
76
|
+
head: headers,
|
|
77
|
+
// Keep internal and horizontal borders, but remove left and right outer borders
|
|
78
|
+
chars: {
|
|
79
|
+
'top': '─', 'top-mid': '┬', 'top-left': '', 'top-right': '',
|
|
80
|
+
'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '', 'bottom-right': '',
|
|
81
|
+
'left': '', 'left-mid': '', 'mid': '─', 'mid-mid': '┼',
|
|
82
|
+
'right': '', 'right-mid': '', 'middle': '│'
|
|
83
|
+
},
|
|
84
|
+
style: {
|
|
85
|
+
head: ['cyan'],
|
|
86
|
+
border: ['grey']
|
|
87
|
+
},
|
|
88
|
+
// Disable cli-table3's wordWrap if we have explicit column widths (since we manually wrapped)
|
|
89
|
+
// Otherwise enable it to let cli-table3 handle auto-sizing
|
|
90
|
+
wordWrap: !options.colWidths,
|
|
91
|
+
wrapOnWordBoundary: false,
|
|
92
|
+
colWidths: options.colWidths
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
processedRows.forEach(row => table.push(row))
|
|
96
|
+
|
|
97
|
+
console.log(table.toString())
|
|
28
98
|
}
|
|
@@ -5,7 +5,7 @@ function getConfig(root) {
|
|
|
5
5
|
const configPath = path.join(root, '.kd', 'config.json')
|
|
6
6
|
|
|
7
7
|
if (!fs.existsSync(configPath)) {
|
|
8
|
-
throw new Error('
|
|
8
|
+
throw new Error('.kd/config.json not found')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
@@ -15,7 +15,7 @@ function getConfigValue(root, key) {
|
|
|
15
15
|
const config = getConfig(root)
|
|
16
16
|
|
|
17
17
|
if (!config[key]) {
|
|
18
|
-
throw new Error(
|
|
18
|
+
throw new Error(`${key} is missing in config.json`)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
return config[key]
|