chenyukai 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict'
4
+
5
+ const { dirname } = require('path');
6
+ const Application = require('../lib/Application'); // 注意这里是 ../lib
7
+
8
+ const COMMANDS_DIR = 'commands';
9
+ // 因为在 bin 目录下,dirname(__dirname) 获取的是项目根目录,这是正确的
10
+ const BASE_PATH = dirname(__dirname);
11
+ const COMMANDS_PATH = BASE_PATH + '/' + COMMANDS_DIR;
12
+ const CONFIG_FILE = 'cy.json';
13
+ const CONFIG_PATH = process.cwd() + '/' + CONFIG_FILE;
14
+
15
+ const application = new Application(COMMANDS_PATH, CONFIG_PATH);
16
+
17
+ application.run().then(exitCode => {
18
+ process.exit(exitCode);
19
+ }).catch(err => {
20
+ console.error(err);
21
+ process.exit(1);
22
+ });
@@ -0,0 +1,62 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ module.exports = async (config, configPath) => {
6
+ // 1. 确定配置文件路径
7
+ // 如果 Application.js 传进来的 configPath 名字不对,这里强制修正为 yc.json
8
+ // 或者你可以直接信任 configPath (建议在 Application.js 里统一修改 CONFIG_FILE = 'cy.json')
9
+ const targetFile = path.join(process.cwd(), 'cy.json');
10
+
11
+ // 2. 检查文件是否已存在
12
+ if (fs.existsSync(targetFile)) {
13
+ console.log(chalk.yellow(`⚠️ 配置文件 ${path.basename(targetFile)} 已存在,跳过创建。`));
14
+ return;
15
+ }
16
+
17
+ // 3. 自动探测打包目录 (Auto-detect build directory)
18
+ // 优先级: ./dist (Vue/React默认) -> ./build (React) -> ./output (Nuxt) -> 默认为 ./dist
19
+ const cwd = process.cwd();
20
+ let detectedPath = './dist'; // 默认值
21
+
22
+ if (fs.existsSync(path.join(cwd, 'dist'))) {
23
+ detectedPath = './dist';
24
+ } else if (fs.existsSync(path.join(cwd, 'build'))) {
25
+ detectedPath = './build';
26
+ } else if (fs.existsSync(path.join(cwd, 'output'))) {
27
+ detectedPath = './output';
28
+ }
29
+
30
+ // 4. 定义模板内容
31
+ const template = {
32
+ "oss": {
33
+ "region": "",
34
+ "bucket": "",
35
+ "accessKeyId": "",
36
+ "accessKeySecret": "",
37
+ "indexPage": "index.html",
38
+ "errorPage": "index.html"
39
+ },
40
+ "ssh": {
41
+ "host": "",
42
+ "prot":"22",
43
+ "username": "root",
44
+ "targetPath": "",
45
+ "privateKey": ""
46
+ },
47
+ "path": detectedPath
48
+ };
49
+
50
+ // 5. 写入文件
51
+ try {
52
+ fs.writeFileSync(targetFile, JSON.stringify(template, null, 2), 'utf8');
53
+
54
+ console.log(chalk.green(`✅ 初始化成功!`));
55
+ console.log(chalk.green(` 已生成配置文件: ${targetFile}`));
56
+ console.log(chalk.cyan(` 已自动识别打包目录为: "${detectedPath}"`));
57
+ console.log(' 请打开文件补充 OSS 或 SSH 的密钥信息。');
58
+
59
+ } catch (error) {
60
+ console.error(chalk.red('❌ 创建配置文件失败:'), error.message);
61
+ }
62
+ };
@@ -0,0 +1,86 @@
1
+ const OSS = require('ali-oss');
2
+ const glob = require('glob');
3
+ const chalk = require('chalk');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ /**
8
+ * OSS 发布逻辑
9
+ * @param {Object} config - 从 cy.json 读取的配置对象
10
+ * @param {String} configPath - cy.json 的文件路径 (备用)
11
+ */
12
+ module.exports = async (config, configPath) => {
13
+ // 1. 校验配置
14
+ const ossConfig = config.oss;
15
+ if (!ossConfig) {
16
+ console.error(chalk.red('❌ 配置错误: cy.json 中缺少 "oss" 字段'));
17
+ return;
18
+ }
19
+
20
+ // 2. 解析本地构建目录 (支持相对路径 ./dist)
21
+ // process.cwd() 确保路径是相对于用户项目的,而不是 CLI 工具的
22
+ const localDir = path.resolve(process.cwd(), config.path || './dist');
23
+
24
+ if (!fs.existsSync(localDir)) {
25
+ console.error(chalk.red(`❌ 目录不存在: ${localDir}`));
26
+ console.error(chalk.yellow(' 请检查 cy.json 中的 "path" 配置,或先执行 npm run build'));
27
+ return;
28
+ }
29
+
30
+ console.log(chalk.blue(`🚀 准备发布到阿里云 OSS Bucket: ${ossConfig.bucket}`));
31
+ console.log(chalk.gray(` 本地目录: ${localDir}`));
32
+
33
+ // 3. 初始化 OSS 客户端
34
+ const client = new OSS({
35
+ region: ossConfig.region,
36
+ accessKeyId: ossConfig.accessKeyId,
37
+ accessKeySecret: ossConfig.accessKeySecret,
38
+ bucket: ossConfig.bucket,
39
+ secure: true // 强制使用 HTTPS
40
+ });
41
+
42
+ // 4. 扫描文件
43
+ // nodir: true 表示只匹配文件,不匹配文件夹本身
44
+ const files = glob.sync('**/*', { cwd: localDir, nodir: true });
45
+
46
+ if (files.length === 0) {
47
+ console.log(chalk.yellow('⚠️ 目录为空,没有文件需要上传。'));
48
+ return;
49
+ }
50
+
51
+ console.log(chalk.cyan(`📦 发现 ${files.length} 个文件,开始上传...`));
52
+
53
+ // 5. 并发上传控制
54
+ // 为了简单直观,这里使用 for...of 串行/并行混合(await 会阻塞当前循环,如果你想极速上传可以用 Promise.all)
55
+ let successCount = 0;
56
+ let failCount = 0;
57
+
58
+ for (const file of files) {
59
+ const localFile = path.join(localDir, file);
60
+
61
+ // 处理 OSS 对象路径:
62
+ // 1. 保持相对结构
63
+ // 2. Windows 系统的 path.sep 是 '\',必须替换为 '/',否则 OSS 会把它当成文件名的一部分
64
+ const objectName = file.split(path.sep).join('/');
65
+
66
+ try {
67
+ await client.put(objectName, localFile);
68
+ console.log(chalk.green(`✔ [OSS] 上传成功: ${objectName}`));
69
+ successCount++;
70
+ } catch (e) {
71
+ console.error(chalk.red(`✖ [OSS] 上传失败: ${objectName}`), e.message);
72
+ failCount++;
73
+ }
74
+ }
75
+
76
+ // 6. 总结
77
+ console.log('\n' + chalk.gray('-'.repeat(40)));
78
+ if (failCount === 0) {
79
+ console.log(chalk.green.bold(`✨ 全部上传成功! (共 ${successCount} 个文件)`));
80
+ if (ossConfig.indexPage) {
81
+ console.log(chalk.blue(`🌍 访问地址: https://${ossConfig.bucket}.${ossConfig.region}.aliyuncs.com/${ossConfig.indexPage}`));
82
+ }
83
+ } else {
84
+ console.log(chalk.yellow.bold(`⚠️ 上传完成,但有 ${failCount} 个文件失败。`));
85
+ }
86
+ };
@@ -0,0 +1,99 @@
1
+ const { NodeSSH } = require('node-ssh');
2
+ const chalk = require('chalk');
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ /**
7
+ * SSH 发布逻辑
8
+ * @param {Object} config - 从 yc.json 读取的配置对象
9
+ */
10
+ module.exports = async (config) => {
11
+ // 1. 校验配置
12
+ const sshConfig = config.ssh;
13
+ if (!sshConfig) {
14
+ console.error(chalk.red('❌ 配置错误: yc.json 中缺少 "ssh" 字段'));
15
+ return;
16
+ }
17
+
18
+ // 2. 解析本地目录
19
+ const localDir = path.resolve(process.cwd(), config.path || './dist');
20
+ if (!fs.existsSync(localDir)) {
21
+ console.error(chalk.red(`❌ 本地目录不存在: ${localDir}`));
22
+ return;
23
+ }
24
+
25
+ // 3. 读取私钥
26
+ let privateKeyContent = null;
27
+ if (sshConfig.privateKey) {
28
+ try {
29
+ const pkPath = path.isAbsolute(sshConfig.privateKey)
30
+ ? sshConfig.privateKey
31
+ : path.resolve(process.cwd(), sshConfig.privateKey);
32
+ privateKeyContent = fs.readFileSync(pkPath, 'utf8');
33
+ } catch (e) {
34
+ console.error(chalk.red(`❌ 无法读取私钥文件: ${sshConfig.privateKey}`));
35
+ return;
36
+ }
37
+ }
38
+
39
+ const ssh = new NodeSSH();
40
+
41
+ // 4. 处理端口显示逻辑 (核心修改)
42
+ // 兼容 port 和 prot 写法,默认为 22
43
+ const rawPort = sshConfig.port || sshConfig.prot || 22;
44
+ const port = parseInt(rawPort);
45
+
46
+ // 拼接显示字符串: 如果是 22 端口就不显示 -p,否则显示 -p 822
47
+ let addressDisplay = `${sshConfig.username}@${sshConfig.host}`;
48
+ if (port !== 22) {
49
+ addressDisplay += ` -p ${port}`;
50
+ }
51
+
52
+ console.log(chalk.magenta(`🔌 正在连接服务器: ${addressDisplay}`));
53
+ console.log(chalk.gray(` 目标路径: ${sshConfig.targetPath}`));
54
+
55
+ try {
56
+ // 5. 连接
57
+ await ssh.connect({
58
+ host: sshConfig.host,
59
+ username: sshConfig.username,
60
+ privateKey: privateKeyContent,
61
+ port: port, // 确保传给 connect 的是数字
62
+ passphrase: sshConfig.passphrase
63
+ });
64
+
65
+ console.log(chalk.green('✔ 连接成功! 准备传输文件...'));
66
+
67
+ // 6. 上传
68
+ const failed = [];
69
+ const successful = [];
70
+
71
+ const status = await ssh.putDirectory(localDir, sshConfig.targetPath, {
72
+ recursive: true,
73
+ concurrency: 10,
74
+ tick: (localPath, remotePath, error) => {
75
+ if (error) {
76
+ failed.push(localPath);
77
+ console.log(chalk.red(`✖ 上传失败: ${path.basename(localPath)}`));
78
+ } else {
79
+ successful.push(localPath);
80
+ console.log(chalk.green(`✔ [SSH] 上传: ${path.basename(localPath)}`));
81
+ }
82
+ }
83
+ });
84
+
85
+ // 7. 结果
86
+ console.log('\n' + chalk.gray('-'.repeat(40)));
87
+ if (status && failed.length === 0) {
88
+ console.log(chalk.green.bold(`✨ SSH 发布成功! (共传输 ${successful.length} 个文件)`));
89
+ } else {
90
+ console.error(chalk.red(`❌ 发布结束,有 ${failed.length} 个文件失败`));
91
+ }
92
+
93
+ } catch (err) {
94
+ console.error(chalk.red('\n💥 SSH 连接或传输错误:'));
95
+ console.error(chalk.red(err.message));
96
+ } finally {
97
+ ssh.dispose();
98
+ }
99
+ };
@@ -0,0 +1,6 @@
1
+ const chalk = require('chalk');
2
+ const packageJson = require('../package.json');
3
+
4
+ module.exports = async () => {
5
+ console.log(chalk.green(`v${packageJson.version}`));
6
+ };
package/index.js ADDED
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1,90 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ class Application {
6
+ constructor(commandsPath, configPath) {
7
+ this.commandsPath = commandsPath;
8
+ this.configPath = configPath;
9
+ }
10
+
11
+ async run() {
12
+ const args = process.argv.slice(2);
13
+ let commandInput = args[0];
14
+
15
+ // 1. 如果没有输入命令,显示帮助
16
+ if (!commandInput) {
17
+ this.showHelp();
18
+ return 0;
19
+ }
20
+
21
+ // 2. 处理版本别名:把 -v 或 --version 统一转成 'version'
22
+ if (['-v', '--version'].includes(commandInput)) {
23
+ commandInput = 'version';
24
+ }
25
+
26
+ // 3. 格式化命令名 (例如: oss-publish -> OssPublish)
27
+ const commandName = this.formatCommandName(commandInput);
28
+ const commandFile = path.join(this.commandsPath, commandName + '.js');
29
+
30
+ // 4. 检查命令文件是否存在
31
+ if (!fs.existsSync(commandFile)) {
32
+ console.error(chalk.red(`\n❌ 未知命令: ${commandInput}`));
33
+ this.showHelp();
34
+ return 1;
35
+ }
36
+
37
+ // 5. 读取配置 (Install 和 Version 命令不需要读取 yc.json)
38
+ let config = {};
39
+ // 定义哪些命令不需要检查配置文件
40
+ const skipConfigCheck = ['install', 'version'];
41
+
42
+ if (!skipConfigCheck.includes(commandInput)) {
43
+ if (fs.existsSync(this.configPath)) {
44
+ try {
45
+ config = require(this.configPath);
46
+ } catch (e) {
47
+ console.error(chalk.yellow('⚠️ 配置文件 yc.json 格式错误'));
48
+ return 1;
49
+ }
50
+ } else {
51
+ console.error(chalk.red('❌ 未找到 yc.json 配置文件,请先运行 "yc install"'));
52
+ return 1;
53
+ }
54
+ }
55
+
56
+ // 6. 统一执行命令
57
+ try {
58
+ const commandFunc = require(commandFile);
59
+ // 传入 config 和 configPath
60
+ await commandFunc(config, this.configPath);
61
+ return 0;
62
+ } catch (error) {
63
+ console.error(chalk.red('\n💥 运行时发生错误:'));
64
+ console.error(error);
65
+ return 1;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * 将 kebab-case (oss-publish) 转换为 PascalCase (OssPublish)
71
+ */
72
+ formatCommandName(name) {
73
+ return name
74
+ .split('-')
75
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
76
+ .join('');
77
+ }
78
+
79
+ showHelp() {
80
+ console.log(chalk.cyan('\n🔥 ChenYu (yc) CLI 使用指南:'));
81
+ console.log('-----------------------------------');
82
+ console.log(` yc install - 初始化配置 (生成 yc.json)`);
83
+ console.log(` yc oss-publish - 发布到阿里云 OSS`);
84
+ console.log(` yc ssh-publish - 发布到 SSH 服务器`);
85
+ console.log(` yc version - 查看当前版本`);
86
+ console.log('-----------------------------------\n');
87
+ }
88
+ }
89
+
90
+ module.exports = Application;
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "chenyukai",
3
+ "version": "1.0.0",
4
+ "description": "Publishing Tools",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "cy": "./bin/index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "Publishing",
14
+ "SSH",
15
+ "OSS",
16
+ "CDN"
17
+ ],
18
+ "author": "ClearSwitch",
19
+ "license": "ISC",
20
+ "dependencies": {
21
+ "ali-oss": "^6.17.1",
22
+ "chalk": "^4.1.2",
23
+ "glob": "^7.2.0",
24
+ "node-ssh": "^13.2.1"
25
+ }
26
+ }