diff --git a/package.json b/package.json index 633c929..2ef09ec 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e58c74d..e781bf2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/src/command/deploy.ts b/src/command/deploy.ts index be0c4cd..b666bf8 100644 --- a/src/command/deploy.ts +++ b/src/command/deploy.ts @@ -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') - .action(() => { + .argument('', '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); diff --git a/src/command/init.ts b/src/command/init.ts index 3589a7f..4046954 100644 --- a/src/command/init.ts +++ b/src/command/init.ts @@ -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)) { diff --git a/src/command/npm.ts b/src/command/npm.ts index b24d6c6..dc48cea 100644 --- a/src/command/npm.ts +++ b/src/command/npm.ts @@ -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); diff --git a/src/command/publish.ts b/src/command/publish.ts index 122c45e..d34bd30 100644 --- a/src/command/publish.ts +++ b/src/command/publish.ts @@ -65,7 +65,7 @@ async function getFiles(cwd: string, patterns: string[]): Promise { return filteredFiles; } -const pack = async () => { +export const pack = async () => { const cwd = process.cwd(); const collection: Record = {}; 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 = {}; 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') @@ -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; outputFilePath: string }; - if (opts.ignore) { - value = await packByIgnore(); - } else { - value = await pack(); - } + let value: { collection: Record; outputFilePath: string } = await packLib(opts.ignore); if (opts.publish && value?.collection) { console.log('\n\npublish'); const { collection, outputFilePath } = value; diff --git a/src/uitls/npm.ts b/src/uitls/npm.ts new file mode 100644 index 0000000..150d036 --- /dev/null +++ b/src/uitls/npm.ts @@ -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 }); + } +};