feat: 添加publish
This commit is contained in:
		| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@kevisual/envision-cli", | ||||
|   "version": "0.0.10", | ||||
|   "version": "0.0.13", | ||||
|   "description": "envision command tools", | ||||
|   "main": "dist/index.js", | ||||
|   "type": "module", | ||||
| @@ -35,7 +35,6 @@ | ||||
|     "fast-glob": "^3.3.2", | ||||
|     "filesize": "^10.1.6", | ||||
|     "form-data": "^4.0.1", | ||||
|     "glob": "^11.0.0", | ||||
|     "ignore": "^6.0.2", | ||||
|     "inquirer": "^12.1.0", | ||||
|     "rimraf": "^6.0.1", | ||||
|   | ||||
							
								
								
									
										3
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @@ -66,9 +66,6 @@ importers: | ||||
|       form-data: | ||||
|         specifier: ^4.0.1 | ||||
|         version: 4.0.1 | ||||
|       glob: | ||||
|         specifier: ^11.0.0 | ||||
|         version: 11.0.0 | ||||
|       ignore: | ||||
|         specifier: ^6.0.2 | ||||
|         version: 6.0.2 | ||||
|   | ||||
| @@ -1,11 +1,14 @@ | ||||
| import { program as app, Command } from '@/program.ts'; | ||||
| import { glob } from 'glob'; | ||||
| import glob from 'fast-glob'; | ||||
| import path from 'path'; | ||||
| import fs from 'fs'; | ||||
| import FormData from 'form-data'; | ||||
| import { baseURL, getBaseURL } from '@/module/query.ts'; | ||||
| import { getBaseURL, query } from '@/module/query.ts'; | ||||
| import { getConfig } from '@/module/index.ts'; | ||||
| import inquirer from 'inquirer'; | ||||
| import { packLib, unpackLib } from './publish.ts'; | ||||
| import chalk from 'chalk'; | ||||
| import { installDeps } from '@/uitls/npm.ts'; | ||||
|  | ||||
| const command = new Command('deploy') | ||||
|   .description('把前端文件传到服务器') | ||||
| @@ -37,8 +40,8 @@ const command = new Command('deploy') | ||||
|       const pwd = process.cwd(); | ||||
|       const directory = path.join(pwd, filePath); | ||||
|       const gPath = path.join(directory, '**/*'); | ||||
|       const files = await glob(gPath, { cwd: pwd, ignore: ['node_modules/**/*'], nodir: true }); | ||||
|       const _relativeFiles = files.map((file) => file.replace(directory + '/', '')); | ||||
|       const files = await glob(gPath, { cwd: pwd, ignore: ['node_modules/**/*'], onlyFiles: true }); | ||||
|       const _relativeFiles = files.map((file) => path.relative(directory, file)); | ||||
|       console.log('upload Files', _relativeFiles); | ||||
|       console.log('upload Files Key', key, version); | ||||
|       if (!yes) { | ||||
| @@ -124,8 +127,40 @@ app.addCommand(command); | ||||
|  | ||||
| const local = new Command('local') | ||||
|   .description('本地部署') | ||||
|   .option('-k, --key <key>', 'key') | ||||
|   .action(() => { | ||||
|   .argument('<key>', 'key') | ||||
|   .option('-i, --ignore', '使用 .npmignore 文件模式去忽略文件进行打包, 不需要package.json中的files字段') | ||||
|   .option('-u, --update', 'query查询 127.0.0.1:11015/api/router?path=local-apps&key=detect') | ||||
|   .action(async (key, opts) => { | ||||
|     console.log('local deploy'); | ||||
|     const { outputFilePath } = await packLib(opts?.ignore); | ||||
|     const mainAppPath = getConfig().mainAppPath; | ||||
|     const appsPath = getConfig().appsPath || path.join(mainAppPath, 'apps'); | ||||
|     if (!key) { | ||||
|       console.error(chalk.red('key is required')); | ||||
|       return; | ||||
|     } | ||||
|     const appPath = path.join(appsPath, key); | ||||
|     if (!fs.existsSync(appPath)) { | ||||
|       fs.mkdirSync(appPath, { recursive: true }); | ||||
|     } | ||||
|     // 复制应用到apps目录下 | ||||
|     if (outputFilePath) { | ||||
|       await unpackLib(outputFilePath, appPath); | ||||
|       fs.unlinkSync(outputFilePath); | ||||
|       installDeps({ appPath }); | ||||
|       if (opts?.update) { | ||||
|         const res = await query.post( | ||||
|           { path: 'local-apps', key: 'detect' }, | ||||
|           { | ||||
|             url: `http://127.0.0.1:11015/api/router?path=local-apps&key=detect`, | ||||
|           }, | ||||
|         ); | ||||
|         if (res.code === 200) { | ||||
|           console.log('local deploy success'); | ||||
|         } else { | ||||
|           console.error('local deploy failed', res.message); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| app.addCommand(local); | ||||
|   | ||||
| @@ -4,7 +4,8 @@ import path from 'path'; | ||||
| import fs from 'fs'; | ||||
| import { chalk } from '@/module/chalk.ts'; | ||||
| import inquirer from 'inquirer'; | ||||
| import { spawn, spawnSync } from 'child_process'; | ||||
| import { spawn } from 'child_process'; | ||||
| import { installDeps } from '@/uitls/npm.ts'; | ||||
|  | ||||
| const command = new Command('init').description('初始化应用').action((optison) => { | ||||
|   console.log('init'); | ||||
| @@ -17,6 +18,7 @@ const setMainAppConfig = async (mainAppPath: string) => { | ||||
|   config.mainAppPath = mainAppPath; | ||||
|   writeConfig(config); | ||||
| }; | ||||
|  | ||||
| const initMain = async () => { | ||||
|   const mainAppPath = path.join(envisionPath, 'main-app'); | ||||
|   const pkgPath = path.join(mainAppPath, 'package.json'); | ||||
| @@ -26,34 +28,29 @@ const initMain = async () => { | ||||
|       name: 'main-app', | ||||
|       version: '1.0.0', | ||||
|       type: 'module', | ||||
|       main: 'dist/app.mjs', | ||||
|       scripts: { | ||||
|         start: 'node dist/app.mjs', | ||||
|       }, | ||||
|       dependencies: { | ||||
|         '@kevisual/router': 'latest', | ||||
|         '@kevisual/local-app-manager': 'latest', | ||||
|         '@kevisual/use-config': 'latest', | ||||
|       }, | ||||
|     }; | ||||
|     fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); | ||||
|     console.log(chalk.green('初始化 main-app 成功')); | ||||
|   } | ||||
|   // 在对应的路径,安装依赖 | ||||
|   const needDeps = ['@kevisual/router', '@kevisual/local-app-manager', '@kevisual/use-config']; | ||||
|   let hasPnpm = false; | ||||
|   try { | ||||
|     spawnSync('pnpm', ['--version']); | ||||
|     hasPnpm = true; | ||||
|   } catch (e) { | ||||
|     hasPnpm = false; | ||||
|   } | ||||
|   for (let dep of needDeps) { | ||||
|     console.log(chalk.green('安装依赖', dep)); | ||||
|     const npm = hasPnpm ? 'pnpm' : 'npm'; | ||||
|     spawnSync(npm, ['install', dep], { cwd: mainAppPath, stdio: 'inherit' }); | ||||
|   } | ||||
|   installDeps({ appPath: mainAppPath, sync: true }); | ||||
|   // 创建 dist/app.mjs | ||||
|   const code = `import { App } from '@kevisual/router'; | ||||
| import { app as LocalApp } from '@kevisual/local-app-manager'; | ||||
| import { useConfig } from '@kevisual/use-config'; | ||||
| const config = useConfig(); | ||||
| const app = new App(); | ||||
| app.importApp(LocalApp); | ||||
| const host = config.host || '127.0.0.1'; | ||||
| app.listen(config.port, host, () => { | ||||
|   console.log(\`app is running on  http://\${host}:\${config.port}\`); | ||||
| LocalApp.listen(config.port, host, () => { | ||||
|   console.log(\`LocalApp is running on  http://\${host}:\${config.port}\`); | ||||
| }); | ||||
| `; | ||||
|   const codeDistPath = path.join(mainAppPath, 'dist'); | ||||
| @@ -92,6 +89,7 @@ app.listen(config.port, host, () => { | ||||
|   // | ||||
|   console.log(chalk.green('创建 app.config.json5 成功')); | ||||
|   setMainAppConfig(mainAppPath); | ||||
|   writeConfig({ ...getConfig(), appsPath: answers.appsPath }); | ||||
| }; | ||||
| const checkPid = (pid: number) => { | ||||
|   try { | ||||
| @@ -124,6 +122,8 @@ const mainApp = new Command('main') | ||||
|     if (options.init) { | ||||
|       await initMain(); | ||||
|       return; | ||||
|     } else { | ||||
|       console.warn(chalk.yellow('请使用 --init 初始化 main 应用')); | ||||
|     } | ||||
|     const runChild = () => { | ||||
|       const logDir = path.join(envisionPath, 'log'); | ||||
| @@ -173,7 +173,7 @@ const mainApp = new Command('main') | ||||
|         } | ||||
|       } | ||||
|       runChild(); | ||||
|     } else if (options.stop) { | ||||
|     } else if (options.exit) { | ||||
|       if (config.mainAppPid) { | ||||
|         // 检查是否有进程 | ||||
|         if (checkPid(config.mainAppPid)) { | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { fileIsExist } from '@/uitls/file.ts'; | ||||
| import { getConfig } from '@/module/get-config.ts'; | ||||
| import fs from 'fs'; | ||||
| import inquirer from 'inquirer'; | ||||
| import { checkPnpm } from '@/uitls/npm.ts'; | ||||
|  | ||||
| const command = new Command('npm').description('npm command show publish and set .npmrc').action(async (options) => {}); | ||||
| const publish = new Command('publish') | ||||
| @@ -165,5 +166,32 @@ const remove = new Command('remove').description('remove .npmrc').action(async ( | ||||
| }); | ||||
| command.addCommand(remove); | ||||
|  | ||||
| // | ||||
| const install = new Command('install') | ||||
|   .option('-n, --noproxy', 'no proxy') | ||||
|   .description('npm install 使用 proxy代理去下载') | ||||
|   .action(async (options) => { | ||||
|     const cwd = process.cwd(); | ||||
|     const config = getConfig(); | ||||
|     let setEnv = {}; | ||||
|     const proxyEnv = { | ||||
|       https_proxy: 'http://127.0.0.1:7890', | ||||
|       http_proxy: 'http://127.0.0.1:7890', | ||||
|       all_proxy: 'socks5://127.0.0.1:7890', | ||||
|       ...config?.proxy, | ||||
|     }; | ||||
|     setEnv = { | ||||
|       ...proxyEnv, | ||||
|     }; | ||||
|     if (options?.noproxy) { | ||||
|       setEnv = {}; | ||||
|     } | ||||
|     if (checkPnpm()) { | ||||
|       spawn('pnpm', ['i'], { stdio: 'inherit', cwd: cwd, env: { ...process.env, ...setEnv } }); | ||||
|     } else { | ||||
|       spawn('npm', ['i'], { stdio: 'inherit', cwd: cwd, env: { ...process.env, ...setEnv } }); | ||||
|     } | ||||
|   }); | ||||
| command.addCommand(install); | ||||
|  | ||||
| // program 添加npm 的命令 | ||||
| program.addCommand(command); | ||||
|   | ||||
| @@ -65,7 +65,7 @@ async function getFiles(cwd: string, patterns: string[]): Promise<string[]> { | ||||
|   return filteredFiles; | ||||
| } | ||||
|  | ||||
| const pack = async () => { | ||||
| export const pack = async () => { | ||||
|   const cwd = process.cwd(); | ||||
|   const collection: Record<string, any> = {}; | ||||
|   const packageJsonPath = path.join(cwd, 'package.json'); | ||||
| @@ -120,6 +120,7 @@ const pack = async () => { | ||||
|   collection.files = allFiles; | ||||
|   collection.packageJson = packageJson; | ||||
|   collection.totalSize = totalSize; | ||||
|   collection.tags = packageJson.app?.tags || packageJson.keywords || []; | ||||
|  | ||||
|   console.log('\nTarball Details'); | ||||
|   console.log(`name:    ${packageJson.name}`); | ||||
| @@ -147,7 +148,7 @@ const pack = async () => { | ||||
|   } | ||||
|   return { collection, outputFilePath }; | ||||
| }; | ||||
| const packByIgnore = async () => { | ||||
| export const packByIgnore = async () => { | ||||
|   let collection: Record<string, any> = {}; | ||||
|   const cwd = process.cwd(); | ||||
|   const patterns = ['**/*']; // 匹配所有文件 | ||||
| @@ -184,6 +185,7 @@ const packByIgnore = async () => { | ||||
|   collection.files = allFiles; | ||||
|   collection.packageJson = packageJson; | ||||
|   collection.totalSize = totalSize; | ||||
|   collection.tags = packageJson.app?.tags || packageJson.keywords || []; | ||||
|  | ||||
|   console.log('\nTarball Details'); | ||||
|   console.log(`package size: ${packageSize}`); | ||||
| @@ -208,6 +210,22 @@ const packByIgnore = async () => { | ||||
|   } | ||||
|   return { collection, outputFilePath }; | ||||
| }; | ||||
| export const packLib = async (ignore: boolean = false) => { | ||||
|   if (ignore) { | ||||
|     return await packByIgnore(); | ||||
|   } | ||||
|   return await pack(); | ||||
| }; | ||||
| export const unpackLib = async (filePath: string, cwd: string) => { | ||||
|   try { | ||||
|     await tar.x({ | ||||
|       file: filePath, | ||||
|       cwd: cwd, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     console.error('Error extracting tarball:', error); | ||||
|   } | ||||
| }; | ||||
| const publishCommand = new Command('publish') | ||||
|   .description('发布应用') | ||||
|   .option('-k, --key <key>', '应用 key') | ||||
| @@ -276,12 +294,7 @@ const packCommand = new Command('pack') | ||||
|   .option('-i, --ignore', '使用 .npmignore 文件模式去忽略文件进行打包, 不需要package.json中的files字段') | ||||
|   .option('-p, --publish', '打包并发布') | ||||
|   .action(async (opts) => { | ||||
|     let value: { collection: Record<string, any>; outputFilePath: string }; | ||||
|     if (opts.ignore) { | ||||
|       value = await packByIgnore(); | ||||
|     } else { | ||||
|       value = await pack(); | ||||
|     } | ||||
|     let value: { collection: Record<string, any>; outputFilePath: string } = await packLib(opts.ignore); | ||||
|     if (opts.publish && value?.collection) { | ||||
|       console.log('\n\npublish'); | ||||
|       const { collection, outputFilePath } = value; | ||||
|   | ||||
							
								
								
									
										30
									
								
								src/uitls/npm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/uitls/npm.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import { spawn, spawnSync } from 'child_process'; | ||||
|  | ||||
| export const checkPnpm = () => { | ||||
|   try { | ||||
|     spawnSync('pnpm', ['--version']); | ||||
|     return true; | ||||
|   } catch (e) { | ||||
|     return false; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| type InstallDepsOptions = { | ||||
|   appPath: string; | ||||
|   isProduction?: boolean; | ||||
|   sync?: boolean; | ||||
| }; | ||||
| export const installDeps = (opts: InstallDepsOptions) => { | ||||
|   const { appPath } = opts; | ||||
|   const isProduction = opts.isProduction ?? true; | ||||
|   const params = ['i']; | ||||
|   if (isProduction) { | ||||
|     params.push('--production'); | ||||
|   } | ||||
|   const syncSpawn = opts.sync ? spawnSync : spawn; | ||||
|   if (checkPnpm()) { | ||||
|     syncSpawn('pnpm', params, { cwd: appPath, stdio: 'inherit', env: process.env }); | ||||
|   } else { | ||||
|     syncSpawn('npm', params, { cwd: appPath, stdio: 'inherit', env: process.env }); | ||||
|   } | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user