@kdcloudjs/cli 0.0.1
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 +22 -0
- package/package.json +28 -0
- package/src/actions/env/auth/index.js +33 -0
- package/src/actions/env/auth/openapi.js +83 -0
- package/src/actions/env/auth/web.js +3 -0
- package/src/actions/env/config.js +102 -0
- package/src/actions/env/create.js +36 -0
- package/src/actions/env/info.js +28 -0
- package/src/actions/env/list.js +21 -0
- package/src/actions/env/remote.js +59 -0
- package/src/actions/env/remove.js +23 -0
- package/src/actions/env/resolveEnv.js +27 -0
- package/src/actions/env/setDefault.js +20 -0
- package/src/actions/project/constants.js +33 -0
- package/src/actions/project/create/createMetaXml.js +26 -0
- package/src/actions/project/create/index.js +24 -0
- package/src/actions/project/create/kwc.js +25 -0
- package/src/actions/project/create/page.js +42 -0
- package/src/actions/project/create/validate.js +45 -0
- package/src/actions/project/create/writeKwcFiles/index.js +15 -0
- package/src/actions/project/create/writeKwcFiles/lwc.js +25 -0
- package/src/actions/project/create/writeKwcFiles/react.js +49 -0
- package/src/actions/project/create/writeKwcFiles/vue.js +48 -0
- package/src/actions/project/deploy/collect.js +43 -0
- package/src/actions/project/deploy/context.js +26 -0
- package/src/actions/project/deploy/index.js +38 -0
- package/src/actions/project/deploy/projectRoot.js +32 -0
- package/src/actions/project/deploy/updateIsv.js +109 -0
- package/src/actions/project/deploy/upload.js +60 -0
- package/src/actions/project/deploy/validate.js +13 -0
- package/src/actions/project/init/index.js +42 -0
- package/src/actions/project/init/post.js +20 -0
- package/src/actions/project/init/prompts.js +50 -0
- package/src/api/index.js +99 -0
- package/src/api/uploader/index.js +7 -0
- package/src/api/uploader/updateKwcMeta.js +90 -0
- package/src/api/uploader/updatePageMeta.js +94 -0
- package/src/commands/env/auth.js +8 -0
- package/src/commands/env/create.js +2 -0
- package/src/commands/env/delete.js +2 -0
- package/src/commands/env/index.js +72 -0
- package/src/commands/env/info.js +2 -0
- package/src/commands/env/list.js +2 -0
- package/src/commands/env/remote.js +2 -0
- package/src/commands/env/set.js +2 -0
- package/src/commands/project/create.js +8 -0
- package/src/commands/project/deploy.js +2 -0
- package/src/commands/project/index.js +28 -0
- package/src/commands/project/init.js +5 -0
- package/src/index.js +24 -0
- package/src/utils/checkUpdate.js +18 -0
- package/src/utils/crypto.js +53 -0
- package/src/utils/download.js +21 -0
- package/src/utils/index.js +9 -0
- package/src/utils/log.js +20 -0
- package/src/utils/printTable.js +28 -0
- package/src/utils/projectConfig.js +32 -0
- package/src/utils/prompts.js +14 -0
- package/src/utils/validator.js +14 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { error } = require('../../../utils/log')
|
|
2
|
+
const { PROJECT_TYPES, FRAMEWORKS } = require('../constants')
|
|
3
|
+
|
|
4
|
+
module.exports = function validate({ name, type, framework }) {
|
|
5
|
+
if (!name) {
|
|
6
|
+
error('Component/Page name cannot be empty')
|
|
7
|
+
process.exit(1)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!type) {
|
|
11
|
+
error('Please specify --type kwc or --type page')
|
|
12
|
+
process.exit(1)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!Object.values(PROJECT_TYPES).includes(type)) {
|
|
16
|
+
error(`Invalid type: ${type}, supported types: kwc | page`)
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 创建的是组件
|
|
21
|
+
if (type === PROJECT_TYPES.KWC) {
|
|
22
|
+
switch (framework) {
|
|
23
|
+
case FRAMEWORKS.LWC:
|
|
24
|
+
if (!/^[a-z][a-zA-Z0-9]*$/.test(name)) {
|
|
25
|
+
error('LWC component name must be lowercase or camelCase')
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
break
|
|
29
|
+
case FRAMEWORKS.React:
|
|
30
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(name) && !/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(name)) {
|
|
31
|
+
error('React component name must follow PascalCase (start with uppercase) or kebab-case (hyphen-separated)')
|
|
32
|
+
process.exit(1)
|
|
33
|
+
}
|
|
34
|
+
break
|
|
35
|
+
case FRAMEWORKS.Vue:
|
|
36
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(name)) {
|
|
37
|
+
error('Vue component name must follow PascalCase (start with uppercase)')
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
break
|
|
41
|
+
default:
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { FRAMEWORKS } = require('../../constants')
|
|
2
|
+
const writeLwcFiles = require('./lwc')
|
|
3
|
+
const writeReactFiles = require('./react')
|
|
4
|
+
const writeVueFiles = require('./vue')
|
|
5
|
+
module.exports = function writeKwcFiles(config) {
|
|
6
|
+
const { framework } = config
|
|
7
|
+
|
|
8
|
+
if (framework === FRAMEWORKS.LWC) {
|
|
9
|
+
writeLwcFiles(config)
|
|
10
|
+
} else if (framework === FRAMEWORKS.React) {
|
|
11
|
+
writeReactFiles(config)
|
|
12
|
+
} else if (framework === FRAMEWORKS.Vue) {
|
|
13
|
+
writeVueFiles(config)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { capitalize } = require('../../../../utils/index')
|
|
4
|
+
const createMetaXml = require('../createMetaXml')
|
|
5
|
+
module.exports = function writeLwcFiles(config) {
|
|
6
|
+
const { dir, name, moduleId, framework } = config
|
|
7
|
+
fs.writeFileSync(
|
|
8
|
+
path.join(dir, `${name}.html`),
|
|
9
|
+
`<template>
|
|
10
|
+
</template>
|
|
11
|
+
`
|
|
12
|
+
)
|
|
13
|
+
fs.writeFileSync(
|
|
14
|
+
path.join(dir, `${name}.js`),
|
|
15
|
+
`import { KingdeeElement } from '@kdcloudjs/kwc';
|
|
16
|
+
|
|
17
|
+
export default class ${capitalize(name)} extends KingdeeElement {
|
|
18
|
+
}
|
|
19
|
+
`
|
|
20
|
+
)
|
|
21
|
+
fs.writeFileSync(
|
|
22
|
+
path.join(dir, `${name}.js-meta.kwc`),
|
|
23
|
+
createMetaXml({ name, moduleId, framework })
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { toPascalCase } = require('../../../../utils/index')
|
|
4
|
+
const createMetaXml = require('../createMetaXml')
|
|
5
|
+
const { LANGUAGES } = require('../../constants')
|
|
6
|
+
|
|
7
|
+
module.exports = function writeReactFiles(config) {
|
|
8
|
+
const { dir, name, moduleId, framework, language } = config
|
|
9
|
+
const componentName = toPascalCase(name)
|
|
10
|
+
|
|
11
|
+
fs.mkdirSync(path.join(dir, '__tests__'), { recursive: true })
|
|
12
|
+
|
|
13
|
+
fs.writeFileSync(
|
|
14
|
+
path.join(dir, `${name}.module.scss`),
|
|
15
|
+
''
|
|
16
|
+
)
|
|
17
|
+
fs.writeFileSync(
|
|
18
|
+
path.join(dir, `${name}.js-meta.kwc`),
|
|
19
|
+
createMetaXml({ name, moduleId, framework })
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if (language === LANGUAGES.JavaScript) {
|
|
23
|
+
fs.writeFileSync(
|
|
24
|
+
path.join(dir, `${name}.jsx`),
|
|
25
|
+
`import styles from './${name}.module.scss';
|
|
26
|
+
|
|
27
|
+
export default function ${componentName} (config) {
|
|
28
|
+
return (
|
|
29
|
+
<div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
`
|
|
34
|
+
)
|
|
35
|
+
} else if (language === LANGUAGES.TypeScript) {
|
|
36
|
+
fs.writeFileSync(
|
|
37
|
+
path.join(dir, `${name}.tsx`),
|
|
38
|
+
`import styles from './${name}.module.scss';
|
|
39
|
+
|
|
40
|
+
export default function ${componentName}(config: KwcConfig) {
|
|
41
|
+
return (
|
|
42
|
+
<div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
`
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { capitalize } = require('../../../../utils/index')
|
|
4
|
+
const createMetaXml = require('../createMetaXml')
|
|
5
|
+
const { LANGUAGES } = require('../../constants')
|
|
6
|
+
|
|
7
|
+
module.exports = function writeVueFiles(config) {
|
|
8
|
+
const { dir, name, moduleId, framework, language } = config
|
|
9
|
+
|
|
10
|
+
fs.mkdirSync(path.join(dir, '__tests__'), { recursive: true })
|
|
11
|
+
|
|
12
|
+
fs.writeFileSync(
|
|
13
|
+
path.join(dir, `${name}.js-meta.kwc`),
|
|
14
|
+
createMetaXml({ name, moduleId, framework })
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if (language === LANGUAGES.JavaScript) {
|
|
18
|
+
fs.writeFileSync(
|
|
19
|
+
path.join(dir, `${name}.ce.vue`),
|
|
20
|
+
`<script setup>
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style lang="scss" scoped>
|
|
29
|
+
</style>
|
|
30
|
+
`
|
|
31
|
+
)
|
|
32
|
+
} else if (language === LANGUAGES.TypeScript) {
|
|
33
|
+
fs.writeFileSync(
|
|
34
|
+
path.join(dir, `${name}.ce.vue`),
|
|
35
|
+
`<script setup lang="ts">
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div>
|
|
40
|
+
</div>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<style lang="scss" scoped>
|
|
44
|
+
</style>
|
|
45
|
+
`
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
function walk(dir, files = []) {
|
|
5
|
+
fs.readdirSync(dir).forEach(name => {
|
|
6
|
+
const full = path.join(dir, name)
|
|
7
|
+
const stat = fs.statSync(full)
|
|
8
|
+
stat.isDirectory()
|
|
9
|
+
? walk(full, files)
|
|
10
|
+
: files.push(full)
|
|
11
|
+
})
|
|
12
|
+
return files
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
exports.collectMeta = function ({ baseDir, projectRoot, hasSourceDir }) {
|
|
16
|
+
const results = []
|
|
17
|
+
|
|
18
|
+
const files = walk(baseDir)
|
|
19
|
+
|
|
20
|
+
files.forEach(file => {
|
|
21
|
+
if (file.endsWith('.js-meta.kwc')) {
|
|
22
|
+
results.push({
|
|
23
|
+
type: 'kwc',
|
|
24
|
+
path: file
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
if (file.endsWith('.page-meta.kwp')) {
|
|
28
|
+
results.push({
|
|
29
|
+
type: 'page',
|
|
30
|
+
path: file
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// 如果不是指定 -d,限制只能上传 app/kwc 和 app/pages
|
|
36
|
+
if (!hasSourceDir) {
|
|
37
|
+
return results.filter(f =>
|
|
38
|
+
f.path.startsWith(path.join(projectRoot, 'app'))
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return results
|
|
43
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const { findProjectRoot, isProjectRoot } = require('./projectRoot')
|
|
3
|
+
|
|
4
|
+
exports.resolveContext = function (options) {
|
|
5
|
+
const cwd = process.cwd()
|
|
6
|
+
const sourceDir = options.sourceDir
|
|
7
|
+
|
|
8
|
+
let baseDir
|
|
9
|
+
if (sourceDir) {
|
|
10
|
+
baseDir = path.isAbsolute(sourceDir)
|
|
11
|
+
? path.resolve(sourceDir)
|
|
12
|
+
: path.resolve(cwd, sourceDir)
|
|
13
|
+
} else {
|
|
14
|
+
baseDir = cwd
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const projectRoot = findProjectRoot(baseDir)
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
cwd,
|
|
21
|
+
baseDir,
|
|
22
|
+
projectRoot,
|
|
23
|
+
hasSourceDir: !!sourceDir,
|
|
24
|
+
isRoot: isProjectRoot(cwd)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
const resolveEnv = require('../../env/resolveEnv')
|
|
3
|
+
const { resolveContext } = require('./context')
|
|
4
|
+
const { validateDeploy } = require('./validate')
|
|
5
|
+
const updateIsv = require('./updateIsv')
|
|
6
|
+
const { collectMeta } = require('./collect')
|
|
7
|
+
const { upload } = require('./upload')
|
|
8
|
+
const { error } = require('../../../utils/log')
|
|
9
|
+
module.exports = async function deploy(options = {}) {
|
|
10
|
+
const { targetEnv } = options
|
|
11
|
+
const { env } = resolveEnv(targetEnv) || {}
|
|
12
|
+
if (!env) return
|
|
13
|
+
if (!env.access_token) {
|
|
14
|
+
error(
|
|
15
|
+
`Environment not authenticated. Please run "kd env auth" first`
|
|
16
|
+
)
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
const ctx = resolveContext(options)
|
|
20
|
+
|
|
21
|
+
validateDeploy(ctx)
|
|
22
|
+
|
|
23
|
+
const files = collectMeta(ctx)
|
|
24
|
+
|
|
25
|
+
if(!files || files.length === 0) {
|
|
26
|
+
error('No files to deploy')
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 更新isv信息到元数据描述文件 updateIsv
|
|
31
|
+
// 在上传前,统一注入 ISV
|
|
32
|
+
updateIsv(files, env?.isv)
|
|
33
|
+
|
|
34
|
+
await upload(files, env)
|
|
35
|
+
|
|
36
|
+
// success('部署完成')
|
|
37
|
+
|
|
38
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
const ROOT_MARKS = [
|
|
5
|
+
'.kd',
|
|
6
|
+
'app/kwc',
|
|
7
|
+
'package.json'
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
function isProjectRoot(dir) {
|
|
11
|
+
return ROOT_MARKS.every(mark =>
|
|
12
|
+
fs.existsSync(path.join(dir, mark))
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function findProjectRoot(startDir) {
|
|
17
|
+
let current = path.resolve(startDir)
|
|
18
|
+
|
|
19
|
+
while (true) {
|
|
20
|
+
if (isProjectRoot(current)) return current
|
|
21
|
+
|
|
22
|
+
const parent = path.dirname(current)
|
|
23
|
+
if (parent === current) return null
|
|
24
|
+
|
|
25
|
+
current = parent
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
isProjectRoot,
|
|
31
|
+
findProjectRoot
|
|
32
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { info, error } = require('../../../utils/log')
|
|
4
|
+
const { findProjectRoot } = require('./projectRoot')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 更新元数据文件中的 <isv> 信息
|
|
8
|
+
* @param {Array} files collectMeta 返回的文件列表
|
|
9
|
+
* @param {string} isv ISV 标识
|
|
10
|
+
*/
|
|
11
|
+
module.exports = function updateIsv(files, isv) {
|
|
12
|
+
if (!isv) return
|
|
13
|
+
try {
|
|
14
|
+
files.forEach(file => {
|
|
15
|
+
if (!['kwc', 'page'].includes(file.type)) return
|
|
16
|
+
|
|
17
|
+
const filePath = file.path
|
|
18
|
+
if (!fs.existsSync(filePath)) return
|
|
19
|
+
|
|
20
|
+
let content = fs.readFileSync(filePath, 'utf-8')
|
|
21
|
+
|
|
22
|
+
// 更新isv标签
|
|
23
|
+
// 已存在 <isv> 标签 → 直接替换
|
|
24
|
+
if (/<isv>[\s\S]*?<\/isv>/.test(content)) {
|
|
25
|
+
content = content.replace(
|
|
26
|
+
/<isv>[\s\S]*?<\/isv>/,
|
|
27
|
+
`<isv>${isv}</isv>`
|
|
28
|
+
)
|
|
29
|
+
} else {
|
|
30
|
+
// 不存在 <isv> 标签 → 插入
|
|
31
|
+
content = injectIsvTag(content, isv)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 更新name标签 这里在name标签前加上isv前缀
|
|
35
|
+
if (file.type === 'page') {
|
|
36
|
+
content = content.replace(
|
|
37
|
+
/<name>([\s\S]*?)<\/name>/,
|
|
38
|
+
(match, p1) => {
|
|
39
|
+
if (p1.startsWith(`${isv}_`)) {
|
|
40
|
+
return match
|
|
41
|
+
}
|
|
42
|
+
return `<name>${isv}_${p1}</name>`
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fs.writeFileSync(filePath, content, 'utf-8')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// 更新根目录下.kd/config.json 填充开发商标识
|
|
51
|
+
const projectRoot = findProjectRoot(process.cwd())
|
|
52
|
+
if (projectRoot) {
|
|
53
|
+
const kdDir = path.join(projectRoot, '.kd')
|
|
54
|
+
const kdConfigPath = path.join(kdDir, 'config.json')
|
|
55
|
+
|
|
56
|
+
let kdConfig = {}
|
|
57
|
+
|
|
58
|
+
if (fs.existsSync(kdConfigPath)) {
|
|
59
|
+
try {
|
|
60
|
+
kdConfig = JSON.parse(fs.readFileSync(kdConfigPath, 'utf-8'))
|
|
61
|
+
} catch {
|
|
62
|
+
// ignore
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
kdConfig.isv = isv
|
|
67
|
+
|
|
68
|
+
if (!fs.existsSync(kdDir)) {
|
|
69
|
+
fs.mkdirSync(kdDir, { recursive: true })
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fs.writeFileSync(
|
|
73
|
+
kdConfigPath,
|
|
74
|
+
JSON.stringify(kdConfig, null, 2),
|
|
75
|
+
'utf-8'
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
// info(`ℹ️ Updated ISV info for ${files.length} metadata files`)
|
|
79
|
+
} catch (e) {
|
|
80
|
+
error(`Failed to update ISV info: ${e.message}`)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 将 <isv> 标签插入到合适位置
|
|
86
|
+
* 优先放在 <name> / <masterLabel> 后面
|
|
87
|
+
*/
|
|
88
|
+
function injectIsvTag(xml, isv) {
|
|
89
|
+
// 常见安全插入点
|
|
90
|
+
const candidates = [
|
|
91
|
+
/<\/masterLabel>/,
|
|
92
|
+
/<\/name>/
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
for (const reg of candidates) {
|
|
96
|
+
if (reg.test(xml)) {
|
|
97
|
+
return xml.replace(
|
|
98
|
+
reg,
|
|
99
|
+
match => `${match}\n <isv>${isv}</isv>`
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// fallback:直接插到根节点内
|
|
105
|
+
return xml.replace(
|
|
106
|
+
/(<[^>]+>)/,
|
|
107
|
+
`$1\n <isv>${isv}</isv>`
|
|
108
|
+
)
|
|
109
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const ora = require('ora')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { updateKwcMeta, updatePageMeta } = require('../../../api/uploader')
|
|
4
|
+
const printTable = require('../../../utils/printTable')
|
|
5
|
+
exports.upload = async function upload(files, env) {
|
|
6
|
+
const spinner = ora(`🚀 Uploading metadata files (${files.length})...`).start()
|
|
7
|
+
console.time(`🕒 Upload duration`)
|
|
8
|
+
|
|
9
|
+
const tasks = files.map(file => {
|
|
10
|
+
return (async () => {
|
|
11
|
+
const record = {
|
|
12
|
+
type: file.type,
|
|
13
|
+
path: file.path,
|
|
14
|
+
status: 'success'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
if (file.type === 'kwc') {
|
|
19
|
+
await updateKwcMeta(file.path, env)
|
|
20
|
+
} else if (file.type === 'page') {
|
|
21
|
+
await updatePageMeta(file.path, env)
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error(`Unsupported metadata type: ${file.type}`)
|
|
24
|
+
}
|
|
25
|
+
} catch (err) {
|
|
26
|
+
record.status = 'failed'
|
|
27
|
+
record.message = err?.message || 'unknown error'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return record
|
|
31
|
+
})()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// ⭐ 核心:并行执行,永不抛异常
|
|
35
|
+
const settledResults = await Promise.allSettled(tasks)
|
|
36
|
+
|
|
37
|
+
spinner.stop()
|
|
38
|
+
|
|
39
|
+
// 提取结果
|
|
40
|
+
const results = settledResults.map(r =>
|
|
41
|
+
r.status === 'fulfilled'
|
|
42
|
+
? r.value
|
|
43
|
+
: {
|
|
44
|
+
type: 'unknown',
|
|
45
|
+
path: '',
|
|
46
|
+
status: 'failed',
|
|
47
|
+
message: r.reason?.message || 'unknown error'
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
const headers = ['Type', 'File', 'Status', 'Message']
|
|
51
|
+
const rows = results.map(file => [
|
|
52
|
+
file.type,
|
|
53
|
+
file.path,
|
|
54
|
+
file.status === 'success' ? '✔' : '✖',
|
|
55
|
+
file.status === 'failed' ? `Failed: ${file.message}` : 'Success'
|
|
56
|
+
])
|
|
57
|
+
printTable(headers, rows)
|
|
58
|
+
console.timeEnd(`🕒 Upload duration`)
|
|
59
|
+
return results
|
|
60
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { error } = require('../../../utils/log')
|
|
2
|
+
|
|
3
|
+
exports.validateDeploy = function (ctx) {
|
|
4
|
+
if (!ctx.projectRoot) {
|
|
5
|
+
error('Current directory is not a KWC project, deployment aborted')
|
|
6
|
+
process.exit(1)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (!ctx.hasSourceDir && !ctx.isRoot) {
|
|
10
|
+
error('Please run in the project root directory or use -d to specify a path')
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const collectInitInfo = require('./prompts')
|
|
4
|
+
const { writeProjectConfig, cleanupGitkeep } = require('./post')
|
|
5
|
+
const downloadTemplate = require('../../../utils/download')
|
|
6
|
+
const { error, info, tip } = require('../../../utils/log')
|
|
7
|
+
const { TEMPLATES, INNER_TEMPLATES } = require('../constants')
|
|
8
|
+
|
|
9
|
+
module.exports = async function initProject({ projectName: _projectName, source }) {
|
|
10
|
+
try {
|
|
11
|
+
const { projectName, moduleId, framework, language } = await collectInitInfo(_projectName) || {}
|
|
12
|
+
|
|
13
|
+
const targetDir = path.resolve(process.cwd(), projectName)
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(targetDir)) {
|
|
16
|
+
error(`Directory already exists: ${projectName}`)
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
tip(`\n🚀 Creating project: ${projectName}\n`)
|
|
21
|
+
console.time('🕒 Project initialization')
|
|
22
|
+
|
|
23
|
+
const templateSource = source === 'outer' ? TEMPLATES[`${framework}_${language}`] : INNER_TEMPLATES[`${framework}_${language}`]
|
|
24
|
+
|
|
25
|
+
await downloadTemplate(
|
|
26
|
+
templateSource,
|
|
27
|
+
targetDir,
|
|
28
|
+
projectName
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
writeProjectConfig(targetDir, { moduleId, framework, language })
|
|
32
|
+
|
|
33
|
+
cleanupGitkeep(targetDir)
|
|
34
|
+
|
|
35
|
+
console.timeEnd('🕒 Project initialization')
|
|
36
|
+
info('next steps:')
|
|
37
|
+
info(` cd ${projectName}`)
|
|
38
|
+
info(' npm install')
|
|
39
|
+
} catch (err) {
|
|
40
|
+
error(err?.message || err)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
exports.writeProjectConfig = function (targetDir, config) {
|
|
5
|
+
const configFile = path.join(targetDir, '.kd', 'config.json')
|
|
6
|
+
fs.mkdirSync(path.dirname(configFile), { recursive: true })
|
|
7
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
exports.cleanupGitkeep = function (targetDir) {
|
|
11
|
+
const gitkeepFiles = [
|
|
12
|
+
path.join(targetDir, 'app', 'pages', '.gitkeep')
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
gitkeepFiles.forEach(file => {
|
|
16
|
+
if (fs.existsSync(file)) {
|
|
17
|
+
fs.unlinkSync(file)
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const { safePrompts } = require('../../../utils/prompts')
|
|
2
|
+
const { FRAMEWORKS, LANGUAGES } = require('../constants')
|
|
3
|
+
|
|
4
|
+
module.exports = async function runPrompts(name) {
|
|
5
|
+
let projectName = name
|
|
6
|
+
|
|
7
|
+
if (!projectName) {
|
|
8
|
+
const res = await safePrompts({
|
|
9
|
+
type: 'text',
|
|
10
|
+
name: 'projectName',
|
|
11
|
+
message: 'Please enter the project name',
|
|
12
|
+
validate: name => name ? true : 'Project name is required'
|
|
13
|
+
})
|
|
14
|
+
projectName = res.projectName
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { framework } = await safePrompts({
|
|
18
|
+
type: 'select',
|
|
19
|
+
name: 'framework',
|
|
20
|
+
message: 'Please choose the framework',
|
|
21
|
+
choices: Object.keys(FRAMEWORKS).map(item => ({
|
|
22
|
+
title: item,
|
|
23
|
+
value: FRAMEWORKS[item]
|
|
24
|
+
}))
|
|
25
|
+
}) || {}
|
|
26
|
+
|
|
27
|
+
// 如果框架是vue或者react,可以选择是用ts,还是js
|
|
28
|
+
let language = LANGUAGES.JavaScript
|
|
29
|
+
if ([FRAMEWORKS.Vue, FRAMEWORKS.React].includes(framework)) {
|
|
30
|
+
const { language: _language } = await safePrompts({
|
|
31
|
+
type: 'select',
|
|
32
|
+
name: 'language',
|
|
33
|
+
message: 'Please choose the language',
|
|
34
|
+
choices: Object.keys(LANGUAGES).map(item => ({
|
|
35
|
+
title: item,
|
|
36
|
+
value: LANGUAGES[item]
|
|
37
|
+
}))
|
|
38
|
+
}) || {}
|
|
39
|
+
language = _language
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { moduleId } = await safePrompts({
|
|
43
|
+
type: 'text',
|
|
44
|
+
name: 'moduleId',
|
|
45
|
+
message: 'Please enter the domain identifier',
|
|
46
|
+
validate: id => id ? true : 'Domain identifier is required'
|
|
47
|
+
}) || {}
|
|
48
|
+
|
|
49
|
+
return { projectName, framework, language, moduleId }
|
|
50
|
+
}
|