import inquirer from 'inquirer'; import postLog from '../service/post.log'; import logger from '../utils/logger'; import { open } from '@miniu/utils'; import { demoList, downloadDemo } from '../service/mini/demo'; import miniAppList from '../service/mini/app.list'; import { TYPES_BUNDLEID } from '../service/mini/util'; import { resolve } from 'path'; import { QRImage, QRToDataURL, QRTerminal } from '../utils/util'; import { getMinidev, initMinidev } from '../service/mini/minidev'; import commander from 'commander'; async function miniAppListAction() { const action = 'MINI_LIST'; // 每次命令,都记录一次日志 postLog({ action, }); try { const list = await miniAppList(); if (!list.length) { console.warn('暂无开通的小程序'); return; } console.log(`该账号下的小程序列表:`); list.forEach((item) => { console.log( `* 名称: ${item.appName}; appId: ${item.appId}; 项目类型: ${item.subApplicationType}` ); }); } catch (e) { logger.error(e, { action, }); process.exit(1); } } // 用户选择列表 interface Choice { name: string; value: any; } interface ChooseDemoResult { apps: Array; } export function promptChooseDemo(choices: Choice[]): Promise { return inquirer.prompt([ { type: 'list', name: 'apps', message: `当前可用${choices.length}个demo小程序:`, choices, }, ]); } async function demoAction(options) { const projectPath = options.path || process.cwd(); const action = 'MINI_DEMO'; // 每次命令,都记录一次日志 postLog({ action, }); try { const list = await demoList(); if (!list.length) { logger.warn('demo小程序列表为空'); return; } const choices = list.map((item) => { return { name: item.name, value: [item.location, item.name], }; }); const promptResult = await promptChooseDemo(choices); const { apps } = promptResult; logger.log('开始下载:', apps[0]); const { message } = await downloadDemo(apps[0], apps[1], projectPath); logger.log(message); } catch (e) { logger.error(e, { action, }); } } async function acceptQrCodeSchema( qrcodeSchema: string, { qrcodeFormat, qrcodeOutput, }: { qrcodeFormat?: 'base64' | 'terminal' | undefined; qrcodeOutput?: string } ) { const fullSchemeUrl = new URL(qrcodeSchema.replace(/&/g, '&')); const qrUrl = fullSchemeUrl.searchParams.get('scheme')!; let qrResult; if (qrcodeFormat === 'base64') { qrResult = await QRToDataURL(qrUrl); console.log(qrResult); } else if (qrcodeFormat === 'terminal') { qrResult = await QRTerminal(qrUrl); } else { qrcodeOutput = qrcodeOutput || resolve('mini.preview.png'); await QRImage(qrcodeOutput!, qrUrl); qrResult = qrcodeOutput; open(`file://${qrcodeOutput}`); } return qrResult; } async function miniPreviewAction(options) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { appId, qrcodeFormat, qrcodeOutput, project, page, launch, clientType, cacheDir, ignoreWebviewCheck, } = options; const action = 'MINI_PREVIEW'; // 每次命令,都记录一次日志 postLog({ action, appId, }); try { const { qrcodeSchema } = await getMinidev().preview({ appId, project, page, query: launch, ignoreHttpReqPermission: ignoreWebviewCheck, ignoreWebViewDomainCheck: ignoreWebviewCheck, cacheDir, clientType, }); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await acceptQrCodeSchema(qrcodeSchema!, { qrcodeFormat, qrcodeOutput }); process.exit(0); } catch (e) { console.error(e); process.exit(-1); } } async function miniDebugAction(options) { const { appId, qrcodeFormat, qrcodeOutput, project, page, launch, clientType, cacheDir, ignoreWebviewCheck, } = options; const action = 'MINI_DEBUG'; // 每次命令,都记录一次日志 postLog({ action, appId, }); try { const { qrcodeSchema } = await getMinidev().remoteDebug({ appId, project, page, query: launch, ignoreHttpReqPermission: ignoreWebviewCheck, ignoreWebViewDomainCheck: ignoreWebviewCheck, cacheDir, clientType, }); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await acceptQrCodeSchema(qrcodeSchema!, { qrcodeFormat, qrcodeOutput }); } catch (e) { console.error(e); process.exit(-1); } } async function miniUploadAction(options) { const { appId, version, project, experience, clientType } = options; const action = 'MINI_UPLOAD'; // 每次命令,都记录一次日志 postLog({ action, appId, }); try { await getMinidev().upload({ appId, version, experience, clientType, project, } as any); process.exit(0); } catch (e) { logger.error(e, { action, }); process.exit(1); } } async function miniBuildAction(options) { const { project, output, cacheDir, appId } = options; try { await getMinidev().build({ output, project, pluginId: appId, cacheDir, }); process.exit(0); } catch (e: any) { console.error(e); process.exit(-1); } } const CLIENT_TYPE_DESC = `上传列表 * ${Object.keys(TYPES_BUNDLEID).join('\n* ')} `; /** * 小程序相关命令 */ export default function registerMiniCommand(program: commander.Command) { initMinidev(); const child = program.command('mini').description('小程序相关操作'); child.command('list').description('获取小程序列表').action(miniAppListAction); child .command('preview') .description('小程序预览') .requiredOption('-i, --app-id ', '小程序appId') .requiredOption('-p, --project ', '项目地址') .option('--disableCache', '云构建无缓存') .option('-l --local-build', '是否采用本地构建后预览') .option('-c, --client-type [clientType]', CLIENT_TYPE_DESC) .option('--page [page]', '入口页面') .option('--launch [launch]', '全局参数,app.js中的onLaunch获取') .option('-f, --qrcode-format [qrcodeFormat]', '返回二维码文件的格式, 默认image') .option('-o, --qrcode-output [qrcodeOutput]', '二维码文件保存路径') .option('--ignore-webview-check [ignoreWebviewCheck]', '忽略 Webview 域名合法性检查') .option('-cache, --cacheDir [cacheDir]', '[可选] 构建缓存路径') .action(miniPreviewAction); child .command('debug') .description('小程序调试') .requiredOption('-i, --app-id ', '小程序appId') .requiredOption('-p, --project ', '项目地址') .option('-l --local-build', '是否采用本地构建后预览') .option('-c, --client-type [clientType]', CLIENT_TYPE_DESC) .option('--page [page]', '入口页面') .option('--launch [launch]', '全局参数,app.js中的onLaunch获取') .option('-f, --qrcode-format [qrcodeFormat]', '返回二维码文件的格式, 默认image') .option('-o, --qrcode-output [qrcodeOutput]', '二维码文件保存路径') .option('--ignore-webview-check [ignoreWebviewCheck]', '忽略 Webview 域名合法性检查') .action(miniDebugAction); child .command('upload') .description('小程序上传') .storeOptionsAsProperties(false) .requiredOption('-i, --app-id ', '小程序appId') .requiredOption('-p, --project ', '项目地址') .option('--pack-path ', '压缩包地址') .option('-c, --client-type [clientType]', CLIENT_TYPE_DESC) .option('-v, --version [version]', '上传包版本') .option( '-e, --experience [experience]', '上传成功后,自动设置为体验版本, 该功能只针对小程序主账号生效' ) .action(miniUploadAction); child .command('demo') .description('下载demo项目到本地') .option('-p,--path [path]', '默认下载到当前目录') .action(demoAction); child .command('build') .description('本地构建小程序') .option('-p, --project ', '项目地址') .option('-o, --output ', '[可选] 产物路径') .option('-appId, --appId ', '[可选] appId, 插件项目必传') .option('-cacheDir, --cacheDir ', '[可选] 构建缓存路径') .action(miniBuildAction); const minidevCommand: commander.Command = (require('minidev') as any).getProgram(); function getMinidevCommand(name: string) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return minidevCommand.commands.find((c) => c.name() === name)!; } child.addCommand(getMinidevCommand('ide')); child.addCommand(getMinidevCommand('dev')); }