@kdcloudjs/cli 0.0.2 → 0.0.4
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/auth/index.js +1 -1
- package/src/actions/env/auth/openapi.js +3 -1
- package/src/actions/env/auth/web.js +1 -1
- 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/index.js +15 -10
- package/src/actions/project/deploy/upload.js +9 -3
- package/src/actions/project/init/index.js +5 -5
- package/src/api/index.js +31 -13
- package/src/api/uploader/updateKwcMeta.js +3 -3
- package/src/api/uploader/updatePageMeta.js +3 -3
- package/src/commands/debug.js +1 -1
- package/src/commands/env/index.js +21 -11
- package/src/commands/project/index.js +9 -4
- package/src/commands/update.js +52 -0
- package/src/index.js +7 -2
- package/src/utils/help.js +18 -0
- package/src/utils/index.js +2 -0
- package/src/utils/printTable.js +94 -24
- package/src/utils/projectConfig.js +11 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kdcloudjs/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
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 = {
|
|
@@ -7,7 +7,7 @@ const openapiAuth = require('./openapi')
|
|
|
7
7
|
const { error } = require('../../../utils/log')
|
|
8
8
|
|
|
9
9
|
module.exports = async function doAuth(options ={}) {
|
|
10
|
-
let { type, env } = options
|
|
10
|
+
let { type, targetEnv: env } = options
|
|
11
11
|
|
|
12
12
|
if (env === undefined) {
|
|
13
13
|
env = loadDefaultEnv()?.name
|
|
@@ -3,7 +3,7 @@ const { getAllDataCenters, getAccessToken, getIsv } = require('../../../api')
|
|
|
3
3
|
const { safePrompts } = require('../../../utils/prompts')
|
|
4
4
|
const { encrypt } = require('../../../utils/crypto')
|
|
5
5
|
const { isValidUrl } = require('../../../utils/validator')
|
|
6
|
-
const { error } = require('../../../utils/log')
|
|
6
|
+
const { error, success } = require('../../../utils/log')
|
|
7
7
|
module.exports = async function authOpenAPI(options = {}) {
|
|
8
8
|
const { env } = options
|
|
9
9
|
|
|
@@ -63,6 +63,8 @@ module.exports = async function authOpenAPI(options = {}) {
|
|
|
63
63
|
error('Failed to obtain access_token. Please check your credentials and reauthentication.')
|
|
64
64
|
return
|
|
65
65
|
}
|
|
66
|
+
success(`Successfully authenticated with OpenAPI.`)
|
|
67
|
+
|
|
66
68
|
|
|
67
69
|
// 获取isv
|
|
68
70
|
const { isv } = await getIsv(url, token) || {}
|
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>controlAp</type>
|
|
40
|
+
<name>controlKey</name>
|
|
41
|
+
<label>组件名称</label>
|
|
42
|
+
<propertys>
|
|
43
|
+
<property>
|
|
44
|
+
<name>StringValue</name>
|
|
45
|
+
<value>hello world</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
|
|
@@ -20,19 +20,24 @@ module.exports = async function deploy(options = {}) {
|
|
|
20
20
|
|
|
21
21
|
validateDeploy(ctx)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
try {
|
|
24
|
+
const files = collectMeta(ctx)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
if(!files || files.length === 0) {
|
|
27
|
+
error('No files to deploy')
|
|
28
|
+
return
|
|
29
|
+
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
// 更新isv信息到元数据描述文件 updateIsv
|
|
32
|
+
// 在上传前,统一注入 ISV
|
|
33
|
+
updateIsv(files, env?.isv)
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
await upload(files, env)
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
// success('部署完成')
|
|
38
|
+
} catch (err) {
|
|
39
|
+
error(err.message)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
37
42
|
|
|
38
43
|
}
|
|
@@ -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
|
@@ -7,7 +7,13 @@ async function getAllDataCenters (url) {
|
|
|
7
7
|
url = url.trim().replace(/\/+$/, '')
|
|
8
8
|
const resp = await fetch(`${url}/auth/getAllDatacenters.do`)
|
|
9
9
|
if (resp?.status === 200) {
|
|
10
|
-
|
|
10
|
+
let data
|
|
11
|
+
try {
|
|
12
|
+
data = await resp.json()
|
|
13
|
+
} catch (e) {
|
|
14
|
+
error(`⚠ Data center response parsing failed, error message: ${e?.message}`)
|
|
15
|
+
return []
|
|
16
|
+
}
|
|
11
17
|
if (Array.isArray(data) && data.length > 0) {
|
|
12
18
|
for (let i = 0; i < data.length; i++) {
|
|
13
19
|
const d = data[i]
|
|
@@ -18,17 +24,17 @@ async function getAllDataCenters (url) {
|
|
|
18
24
|
value: d.accountId
|
|
19
25
|
})
|
|
20
26
|
} else {
|
|
21
|
-
error(`⚠
|
|
27
|
+
error(`⚠ Data center response error, please check if url: ${url} is correct`)
|
|
22
28
|
}
|
|
23
29
|
}
|
|
24
30
|
} else {
|
|
25
|
-
error(`⚠
|
|
31
|
+
error(`⚠ Data center list is empty, please check if url: ${url} is correct`)
|
|
26
32
|
}
|
|
27
33
|
} else {
|
|
28
|
-
error(`⚠
|
|
34
|
+
error(`⚠ Request failed, please check if url: ${url} is correct. error message: ${resp?.statusText}`)
|
|
29
35
|
}
|
|
30
36
|
} catch (e) {
|
|
31
|
-
error(`⚠
|
|
37
|
+
error(`⚠ Request failed, please check if url: ${url} is correct. error message: ${e?.message}`)
|
|
32
38
|
}
|
|
33
39
|
return res
|
|
34
40
|
}
|
|
@@ -54,17 +60,23 @@ async function getAccessToken (url, params) {
|
|
|
54
60
|
body: JSON.stringify(body)
|
|
55
61
|
})
|
|
56
62
|
if (resp?.status === 200) {
|
|
57
|
-
|
|
63
|
+
let respData
|
|
64
|
+
try {
|
|
65
|
+
respData = await resp.json()
|
|
66
|
+
} catch (e) {
|
|
67
|
+
error(`⚠ Token response parsing failed, error message: ${e?.message}`)
|
|
68
|
+
return {}
|
|
69
|
+
}
|
|
58
70
|
if (respData?.status) {
|
|
59
71
|
res = respData?.data || {}
|
|
60
72
|
} else {
|
|
61
|
-
error(`⚠
|
|
73
|
+
error(`⚠ Failed to get access_token, please check url, client_id, client_secret, username. error message: ${respData?.message}`)
|
|
62
74
|
}
|
|
63
75
|
} else {
|
|
64
|
-
error(`⚠
|
|
76
|
+
error(`⚠ Failed to get access_token, please check url, client_id, client_secret, username. response status: ${resp?.status}`)
|
|
65
77
|
}
|
|
66
78
|
} catch (e) {
|
|
67
|
-
error(`⚠
|
|
79
|
+
error(`⚠ Failed to get access_token, please check url, client_id, client_secret, username. error message: ${e?.message}`)
|
|
68
80
|
}
|
|
69
81
|
return res
|
|
70
82
|
}
|
|
@@ -81,17 +93,23 @@ async function getIsv (url, access_token) {
|
|
|
81
93
|
})
|
|
82
94
|
|
|
83
95
|
if (resp.status === 200) {
|
|
84
|
-
|
|
96
|
+
let respData
|
|
97
|
+
try {
|
|
98
|
+
respData = await resp.json()
|
|
99
|
+
} catch (e) {
|
|
100
|
+
error(`⚠ ISV response parsing failed, error message: ${e?.message}`)
|
|
101
|
+
return {}
|
|
102
|
+
}
|
|
85
103
|
if (respData?.status) {
|
|
86
104
|
res = respData?.data || {}
|
|
87
105
|
} else {
|
|
88
|
-
error(`⚠ get isv fail
|
|
106
|
+
error(`⚠ get isv fail, pls check url and credentials info, errormessage: ${respData?.message}`)
|
|
89
107
|
}
|
|
90
108
|
} else {
|
|
91
|
-
error(`⚠ get isv fail
|
|
109
|
+
error(`⚠ get isv fail, pls check url and credentials info, response status: ${resp?.status}`)
|
|
92
110
|
}
|
|
93
111
|
} catch (err) {
|
|
94
|
-
error(`⚠ getIsv fail
|
|
112
|
+
error(`⚠ getIsv fail. error message: ${err.message}`)
|
|
95
113
|
}
|
|
96
114
|
return res
|
|
97
115
|
}
|
|
@@ -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
|
}
|
package/src/commands/debug.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module.exports = function registerDebug(program) {
|
|
2
2
|
program
|
|
3
3
|
.command('debug')
|
|
4
|
-
.description('
|
|
4
|
+
.description('debug project')
|
|
5
5
|
.option('-e, --target-env <env>', 'specified target environment')
|
|
6
6
|
.action((options) =>
|
|
7
7
|
require('../actions/debug')(options)
|
|
@@ -1,53 +1,63 @@
|
|
|
1
|
+
const KdHelp = require('../../utils/help')
|
|
2
|
+
|
|
1
3
|
module.exports = function registerEnv(program) {
|
|
2
4
|
const env = program
|
|
3
5
|
.command('env')
|
|
4
|
-
.description('
|
|
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>')
|
|
8
|
-
.description('
|
|
13
|
+
.description('create a new environment')
|
|
9
14
|
.option('--url <url>', 'environment url')
|
|
10
15
|
.action((name, options) =>
|
|
11
16
|
require('./create')(name, options)
|
|
12
17
|
)
|
|
13
18
|
|
|
14
|
-
env
|
|
19
|
+
const set = env
|
|
15
20
|
.command('set')
|
|
16
|
-
.description('
|
|
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
|
-
.description('
|
|
28
|
+
.description('set default environment')
|
|
19
29
|
.action(name =>
|
|
20
30
|
require('./set')(name)
|
|
21
31
|
)
|
|
22
32
|
|
|
23
33
|
env
|
|
24
34
|
.command('info')
|
|
25
|
-
.description('
|
|
35
|
+
.description('show current environment info')
|
|
26
36
|
.action(() =>
|
|
27
37
|
require('./info')()
|
|
28
38
|
)
|
|
29
39
|
|
|
30
40
|
env
|
|
31
41
|
.command('list')
|
|
32
|
-
.description('
|
|
42
|
+
.description('list all environments')
|
|
33
43
|
.action(() =>
|
|
34
44
|
require('./list')()
|
|
35
45
|
)
|
|
36
46
|
|
|
37
47
|
env
|
|
38
48
|
.command('delete <name>')
|
|
39
|
-
.description('
|
|
49
|
+
.description('delete an environment')
|
|
40
50
|
.action(name =>
|
|
41
51
|
require('./delete')(name)
|
|
42
52
|
)
|
|
43
53
|
|
|
44
54
|
const auth = env
|
|
45
55
|
.command('auth')
|
|
46
|
-
.description('
|
|
56
|
+
.description('authenticate environment')
|
|
47
57
|
|
|
48
58
|
auth
|
|
49
59
|
.command('web')
|
|
50
|
-
.description('
|
|
60
|
+
.description('authenticate via Web (username/password)')
|
|
51
61
|
.option('-e --target-env <name>', 'target environment name')
|
|
52
62
|
.action((options) =>
|
|
53
63
|
require('./auth')('web', options)
|
|
@@ -55,7 +65,7 @@ module.exports = function registerEnv(program) {
|
|
|
55
65
|
|
|
56
66
|
auth
|
|
57
67
|
.command('openapi')
|
|
58
|
-
.description('
|
|
68
|
+
.description('authenticate via OpenAPI (client credentials)')
|
|
59
69
|
.option('-e --target-env <name>', 'target environment name')
|
|
60
70
|
.action((options) =>
|
|
61
71
|
require('./auth')('openapi', options)
|
|
@@ -1,17 +1,22 @@
|
|
|
1
|
+
const KdHelp = require('../../utils/help')
|
|
2
|
+
|
|
1
3
|
module.exports = function registerProject(program) {
|
|
2
4
|
const project = program
|
|
3
5
|
.command('project')
|
|
4
|
-
.description('
|
|
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>')
|
|
8
|
-
.description('
|
|
13
|
+
.description('initialize a new project')
|
|
9
14
|
.option('-s, --source <source>', 'specified repository source', 'outer')
|
|
10
15
|
.action((name, options) => require('./init')(name, options))
|
|
11
16
|
|
|
12
17
|
project
|
|
13
18
|
.command('create <name>')
|
|
14
|
-
.description('
|
|
19
|
+
.description('create control or page xml')
|
|
15
20
|
.option('--type <type>', 'project type: kwc | page')
|
|
16
21
|
.action((name, options) =>
|
|
17
22
|
require('./create')(name, options)
|
|
@@ -19,7 +24,7 @@ module.exports = function registerProject(program) {
|
|
|
19
24
|
|
|
20
25
|
project
|
|
21
26
|
.command('deploy')
|
|
22
|
-
.description('
|
|
27
|
+
.description('deploy current project')
|
|
23
28
|
.option('-e --target-env <env>', 'specified target environment')
|
|
24
29
|
.option('-d --source-dir <dir>', 'specified source directory to deploy')
|
|
25
30
|
// .option('-f --force', 'force deploy, ignore warnings')
|
|
@@ -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/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
exports.capitalize = function (name) {
|
|
2
|
+
if (!name || typeof name !== 'string') return ''
|
|
2
3
|
return name[0].toUpperCase() + name.slice(1)
|
|
3
4
|
}
|
|
4
5
|
|
|
5
6
|
exports.toPascalCase = function (name) {
|
|
7
|
+
if (!name || typeof name !== 'string') return ''
|
|
6
8
|
return name
|
|
7
9
|
.replace(/(-\w)/g, (match) => match[1].toUpperCase()) // kebab-case to camelCase
|
|
8
10
|
.replace(/^[a-z]/, (match) => match.toUpperCase()) // first char to Upper
|
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
|
}
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
|
+
const { error } = require('./log')
|
|
3
4
|
|
|
4
5
|
function getConfig(root) {
|
|
5
6
|
const configPath = path.join(root, '.kd', 'config.json')
|
|
6
7
|
|
|
7
8
|
if (!fs.existsSync(configPath)) {
|
|
8
|
-
|
|
9
|
+
error('.kd/config.json not found, please run "kd project init" first')
|
|
10
|
+
process.exit(1)
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
15
|
+
} catch (e) {
|
|
16
|
+
error('Failed to parse .kd/config.json, please check if the file is valid JSON')
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
function getConfigValue(root, key) {
|
|
15
22
|
const config = getConfig(root)
|
|
16
23
|
|
|
17
|
-
if (!config[key]) {
|
|
18
|
-
throw new Error(
|
|
24
|
+
if (!config || !config[key]) {
|
|
25
|
+
throw new Error(`${key} is missing in config.json`)
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
return config[key]
|