diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7c5b4b9..e53497e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,25 +22,25 @@ jobs: manifest-file: .release-please-manifest.json - name: 检出代码 uses: actions/checkout@v4 - if: ${{ steps.release.outputs['packages/core--release_created'] || steps.release.outputs['packages/cli--release_created'] }} + if: ${{ steps.release.outputs['packages/core--release_created'] || steps.release.outputs['packages/cli--release_created'] || steps.release.outputs['packages/create-karin--release_created'] }} - name: 设置 node uses: actions/setup-node@v4 with: node-version: 20 - registry-url: "https://registry.npmjs.org" - if: ${{ steps.release.outputs['packages/core--release_created'] || steps.release.outputs['packages/cli--release_created'] }} + registry-url: 'https://registry.npmjs.org' + if: ${{ steps.release.outputs['packages/core--release_created'] || steps.release.outputs['packages/cli--release_created'] || steps.release.outputs['packages/create-karin--release_created'] }} - name: 设置 pnpm uses: pnpm/action-setup@v2 with: version: 8 - if: ${{ steps.release.outputs['packages/core--release_created'] }} + if: ${{ steps.release.outputs['packages/core--release_created'] || steps.release.outputs['packages/create-karin--release_created'] }} # 安装所有依赖 - name: 安装依赖 run: pnpm install - if: ${{ steps.release.outputs['packages/core--release_created'] }} + if: ${{ steps.release.outputs['packages/core--release_created'] || steps.release.outputs['packages/create-karin--release_created'] }} # 发布 core 包 - name: 发布 core 包 @@ -57,4 +57,11 @@ jobs: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} working-directory: packages/cli run: pnpm pub - \ No newline at end of file + + # 发布 create-karin 包 + - name: 发布 create-karin 包 + if: ${{ steps.release.outputs['packages/create-karin--release_created'] }} + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + working-directory: packages/create-karin + run: pnpm build && pnpm pub diff --git a/.release-please-config.json b/.release-please-config.json index c928252..f581f4a 100644 --- a/.release-please-config.json +++ b/.release-please-config.json @@ -74,7 +74,14 @@ "component": "cli", "tag-separator": "-", "scope": "cli" + }, + "packages/create-karin": { + "release-type": "node", + "package-name": "create-karin", + "component": "create-karin", + "tag-separator": "-", + "scope": "create-karin" } }, "commit-search-depth": 50 -} \ No newline at end of file +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4721053..06f3ecd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,5 @@ { - "packages/core": "1.1.1", - "packages/cli": "1.0.0" -} \ No newline at end of file + "packages/core": "1.1.0", + "packages/cli": "1.0.0", + "packages/create-karin": "1.0.0" +} diff --git a/packages/cli/package.json b/packages/cli/package.json index a0ff51c..eb4fd0a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -6,6 +6,13 @@ "bin": { "karin": "index.js" }, + "engines": { + "node": ">=18" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, "scripts": { "pub": "npm publish --access public" }, @@ -18,4 +25,4 @@ ], "author": "", "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/core/exports/cli/exec.ts b/packages/core/exports/cli/exec.ts index 65b24f5..be97b52 100644 --- a/packages/core/exports/cli/exec.ts +++ b/packages/core/exports/cli/exec.ts @@ -1,7 +1,4 @@ -import { - exec as execCmd, - execSync as execSyncCmd, -} from 'node:child_process' +import { exec as execCmd, execSync as execSyncCmd } from 'node:child_process' /** * 执行命令 @@ -10,7 +7,7 @@ import { */ const execSync = ( cmd: string, - options: import('node:child_process').ExecOptions = {} + options: import('node:child_process').ExecOptions = {}, ): { status: boolean; error: Error | null; stdout: string; stderr: string } => { try { const result = execSyncCmd(cmd, options) @@ -27,9 +24,9 @@ const execSync = ( */ const exec = ( cmd: string, - options: import('node:child_process').ExecOptions = {} + options: import('node:child_process').ExecOptions = {}, ): Promise<{ status: boolean; error: Error | null; stdout: string; stderr: string }> => { - return new Promise((resolve) => { + return new Promise(resolve => { execCmd(cmd, options, (error, stdout, stderr) => { const status = !error resolve({ status, error, stdout, stderr }) diff --git a/packages/core/exports/cli/init.ts b/packages/core/exports/cli/init.ts index e65d3c3..3a8d8ce 100644 --- a/packages/core/exports/cli/init.ts +++ b/packages/core/exports/cli/init.ts @@ -42,7 +42,7 @@ export const createDir = () => { path.join(dir, '@karinjs', 'temp', 'html'), ] - isDev && list.push(path.join(dir, 'plugins', 'karin-plugin-example')) + !isDev && list.push(path.join(dir, 'plugins', 'karin-plugin-example')) list.forEach(item => { if (!fs.existsSync(item)) fs.mkdirSync(item, { recursive: true }) }) @@ -206,8 +206,8 @@ const createWorkspace = (dir: string) => { * 生成一些其他文件 */ export const createOtherFile = async () => { - isDev && createPnpmFile(dir) - isDev && createWorkspace(dir) + !isDev && createPnpmFile(dir) + !isDev && createWorkspace(dir) if (!shouldSkipNpmrc()) { createOrUpdateNpmrc(dir) @@ -259,9 +259,11 @@ export const modifyPackageJson = () => { data.scripts.karin = 'karin' const list = ['app', 'start', 'pm2', 'stop', 'rs', 'log'] - list.forEach(v => { - data.scripts[v] = `karin ${v}` - }) + if (!isDev) { + list.forEach(v => { + data.scripts[v] = `karin ${v}` + }) + } fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(data, null, 2)) return data diff --git a/packages/create-karin/package.json b/packages/create-karin/package.json index 2106cf9..744e2a4 100644 --- a/packages/create-karin/package.json +++ b/packages/create-karin/package.json @@ -8,9 +8,17 @@ "bin": { "create-karin": "./lib/index.js" }, + "engines": { + "node": ">=18" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, "scripts": { "dev": "tsx src/index.ts", - "build": "tsup --config tsup.config.ts" + "build": "tsup --config tsup.config.ts", + "pub": "npm publish --access public" }, "keywords": [ "karin", @@ -28,5 +36,9 @@ "@types/prompts": "^2.4.9", "tsup": "^8.0.1", "typescript": "^5.3.3" - } -} \ No newline at end of file + }, + "files": [ + "lib", + "templates" + ] +} diff --git a/packages/create-karin/src/dev.ts b/packages/create-karin/src/dev.ts new file mode 100644 index 0000000..a2120d9 --- /dev/null +++ b/packages/create-karin/src/dev.ts @@ -0,0 +1,99 @@ +import fs from 'node:fs' +import path from 'node:path' +import { green, magenta, red, yellow } from 'kolorist' +import { checkNetwork, installPuppeteer } from './project' +import { getStr } from './utils/tools' + +/** + * 创建开发环境项目 + * @param name - 项目名称 + * @param type - 项目类型 + */ +export const copyTemplate = (name: string, type: 'ts' | 'js') => { + console.log('📦 正在创建项目目录结构...') + + /** karin目录 */ + const dir = path.join(process.cwd(), name) + /** ts模板路径 */ + const templatePath = path.join(__dirname, `../templates/${type}-plugin`) + + // 直接复制整个模板目录 + fs.cpSync(templatePath, dir, { recursive: true }) + + // TODO: 后续改为远程api拉取 动态更新 + const list = [ + 'node_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/sqlite3', + 'better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3', + 'sass_binary_site=https://registry.npmmirror.com/-/binary/node-sass', + 'sharp_binary_host=https://registry.npmmirror.com/-/binary/sharp', + 'sharp_libvips_binary_host=https://registry.npmmirror.com/-/binary/sharp-libvips', + 'canvas_binary_host_mirror=https://registry.npmmirror.com/-/binary/canvas', + '# 19以下版本', + 'puppeteer_download_host=https://registry.npmmirror.com/mirrors', + '# 20以上版本', + 'PUPPETEER_DOWNLOAD_BASE_URL = https://registry.npmmirror.com/binaries/chrome-for-testing', + ] + + const npmrc = path.join(dir, '.npmrc') + fs.writeFileSync(npmrc, list.join('\n')) + + /** 将.env文件的两个token随机生成 */ + const env = path.join(dir, '.env') + const envContent = fs.readFileSync(env, 'utf-8') + const token = getStr(5) + const wsToken = getStr(5) + + fs.writeFileSync( + env, + envContent + .replace('HTTP_AUTH_KEY=QtYTbI', `HTTP_AUTH_KEY=${token}`) + .replace('WS_SERVER_AUTH_KEY=QOnAho', `WS_SERVER_AUTH_KEY=${wsToken}`), + ) + + console.log(green('✨ 项目目录结构创建完成')) +} + +/** + * 创建开发环境项目 + * @param name - 项目名称 + * @param type - 项目类型 + * @param puppeteer - 是否安装Puppeteer + */ +export const createDev = async (name: string, type: 'ts' | 'js', puppeteer: boolean) => { + try { + copyTemplate(name, type) + + if (puppeteer) { + const isNpmMirror = await checkNetwork() + installPuppeteer(name, isNpmMirror) + } + + const list = [ + '\n\n--------------------------------', + '\n✨ 项目创建成功!', + yellow('👇 请执行以下命令:\n'), + green(` cd ${name}`), + green(' pnpm dev\n'), + ' 快捷指令(上下任选其一):', + magenta(` cd ${name} && pnpm dev\n`), + '🚀 开始愉快的开发吧!', + ] + + if (puppeteer) { + list.push( + '--------------------------------', + ' 如需使用Puppeteer,请执行以下命令:', + green(` cd ${name}-puppeteer && node .`), + '--------------------------------', + ) + } + + console.log(list.join('\n')) + } catch (error) { + console.log(magenta('Github: https://github.com/Karinjs/Karin/issues')) + console.log(red('发生错误,请将以下信息反馈给开发者:')) + console.log(green('--------------------------------')) + console.log(error) + console.log(green('--------------------------------')) + } +} diff --git a/packages/create-karin/src/index.ts b/packages/create-karin/src/index.ts index d8755f7..bb407ae 100644 --- a/packages/create-karin/src/index.ts +++ b/packages/create-karin/src/index.ts @@ -2,30 +2,32 @@ import fs from 'node:fs' import path from 'node:path' -import { existsSync } from 'node:fs' -import { fileURLToPath } from 'node:url' - import prompts from 'prompts' -import { getStr } from './tools' -import { green, red, yellow, magenta } from 'kolorist' -import { pingUrls, checkPnpm, installPnpm } from './tasks' +import { green, red, yellow } from 'kolorist' + +import { production } from './project' +import { getStr } from './utils/tools' +import { createDev } from './dev' /** - * 创建新项目 + * 验证项目名称 */ -const createProject = async () => { - const validateProjectName = (value: string) => { - const dir = path.join(process.cwd(), value) - /** 不允许中文 */ - if (/[\u4e00-\u9fa5]/.test(value)) { - return '项目名称不允许包含中文' - } - if (existsSync(dir)) { - return `目录 ${value} 已存在,推荐使用 ${value}-${getStr(5)}` - } - return true +export const validateProjectName = (value: string) => { + const dir = path.join(process.cwd(), value) + /** 不允许中文 */ + if (/[\u4e00-\u9fa5]/.test(value)) { + return '项目名称不允许包含中文' + } + if (fs.existsSync(dir)) { + return `目录 ${value} 已存在,推荐使用 ${value}-${getStr(5)}` } + return true +} +/** + * 创建新项目 + */ +const createProject = async () => { const response = await prompts([ { type: 'text', @@ -47,108 +49,48 @@ const createProject = async () => { }, { title: 'TypeScript 开发环境', - value: 'ts-plugin', + value: 'ts', description: '用于开发 TypeScript 插件', }, { title: 'JavaScript 开发环境', - value: 'js-plugin', + value: 'js', description: '用于开发 JavaScript 插件', }, ], }, + { + type: 'select', + name: 'installPuppeteer', + message: '是否安装 Puppeteer?(已有无需安装)', + initial: 0, + choices: [ + { + title: `${green('是')} ${yellow('(推荐)')}`, + value: true, + description: '安装 Puppeteer 以支持浏览器自动化', + }, + { + title: '否', + value: false, + description: '不安装 Puppeteer', + }, + ], + }, ]) if (!response.projectName || !response.template) { throw new Error('操作已取消') } - const template = response.template - const targetDir = path.join(process.cwd(), response.projectName) - - // 检查网络环境 - console.log('检查网络环境...') - const networkResult = await pingUrls() - if (networkResult.ping) { - console.log(green('网络环境极佳 ^_^')) - } else { - console.log(red('网络环境较差 将使用镜像源安装依赖~')) - } - - // 检查并安装 pnpm - console.log('检查 pnpm...') - const pnpm = await checkPnpm() - if (!pnpm) { - console.log('正在安装 pnpm...') - await installPnpm(networkResult.suffix) - console.log('pnpm 安装成功') - } - - // 创建项目 - console.log('正在创建项目...') - /** 生产环境 */ - if (template === 'production') { - const pkg = { - name: response.projectName, - version: '1.0.0', - description: 'Karin Bot', - type: 'module', - } - await fs.promises.writeFile(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2)) - } else { - const templateDir = path.join( - fileURLToPath(import.meta.url), - '../../templates', - template - ) - - await fs.promises.mkdir(targetDir, { recursive: true }) - await fs.promises.cp(templateDir, targetDir, { recursive: true }) - } - - const projectName = response.projectName - const main = template === 'production' ? 'pnpm app' : 'pnpm dev' - - console.log('\n📦 正在安装依赖...') - const { execSync } = await import('node:child_process') - try { - execSync('pnpm install -P', { stdio: 'inherit', cwd: targetDir }) - console.log(green('\n✨ 依赖安装完成!')) - - /** 初始化项目 */ - execSync('npx karin init', { stdio: 'inherit', cwd: targetDir }) - - /** 如果非生产环境 则删除掉一些文件 */ - if (template !== 'production') { - const list = [ - 'plugins', - 'pnpm-workspace.yaml', - '.pnpmfile.cjs', - ] - - for (const file of list) { - fs.rmSync(file, { recursive: true }) - } - } - - console.log('\n🚀 正在启动项目...') - execSync(main, { stdio: 'inherit' }) - } catch (error) { - console.log(red('\n❌ 自动安装失败,请手动执行以下命令:')) + if (response.template === 'production') { + await production(response.projectName, response.installPuppeteer) + return } - console.log([ - '\n✨ 项目创建成功!', - yellow('👇 请执行以下命令:\n'), - green(` cd ${projectName}`), - green(` ${main}\n`), - ' 快捷指令(上下任选其一):', - magenta(` cd ${projectName} && ${main}\n`), - template === 'production' - ? '🚀 开始愉快的使用吧!' - : '🚀 开始愉快的开发吧!', - ].filter(Boolean).join('\n')) + /** 开发环境 */ + await createDev(response.projectName, response.template, response.installPuppeteer) } /** diff --git a/packages/create-karin/src/project.ts b/packages/create-karin/src/project.ts new file mode 100644 index 0000000..bfedeac --- /dev/null +++ b/packages/create-karin/src/project.ts @@ -0,0 +1,179 @@ +import fs from 'node:fs' +import path from 'node:path' +import { execSync } from './utils/exec' +import { pingUrls } from './utils/tasks' +import { green, magenta, red, yellow } from 'kolorist' + +/** + * 创建生产环境项目配置 + */ +export const createProductionConfig = (projectName: string) => ({ + name: projectName, + version: '1.0.0', + description: 'Karin Bot', + type: 'module', + main: './node_modules/node-karin/dist/index.js', +}) + +/** + * 获取后缀 + * @param isNpmMirror - 是否使用镜像源 + */ +export const getSuffix = (isNpmMirror: boolean) => { + return isNpmMirror ? ' --registry=https://registry.npmmirror.com' : '' +} + +/** + * 创建项目目录结构 + * @param name - 项目名称 + * @param isNpmMirror - 是否使用镜像源 + */ +export const createProjectStructure = async (name: string, isNpmMirror: boolean) => { + console.log('📦 正在创建项目目录结构...') + + /** karin目录 */ + const dir = path.join(process.cwd(), name) + fs.mkdirSync(dir, { recursive: true }) + + const pkg = createProductionConfig(name) + fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(pkg, null, 2)) + console.log(green('✨ 项目目录结构创建完成')) + + console.log('📦 正在安装最新版本的node-karin...') + const cmd = `pnpm install node-karin@latest${getSuffix(isNpmMirror)}` + execSync(cmd, { cwd: dir, stdio: 'inherit' }) + console.log(green('✨ node-karin 安装成功')) + + console.log('📦 正在执行初始化...') + execSync('npx karin init', { cwd: dir, stdio: 'inherit' }) + console.log(green('✨ 初始化完成')) +} + +/** + * 安装puppeteer + * @param dir - 项目名称 + * @param isNpmMirror - 是否使用镜像源 + */ +export const installPuppeteer = async (name: string, isNpmMirror: boolean) => { + /** karin目录 */ + const karinDir = path.join(process.cwd(), name) + /** puppeteer目录 */ + const puppeteerDir = path.join(process.cwd(), `${name}-puppeteer`) + + const envContent = fs.readFileSync(path.join(karinDir, '.env'), 'utf-8') + /** ws server token */ + const wsAuthKey = envContent.match(/WS_SERVER_AUTH_KEY=(.+)/)?.[1] || '123456' + /** http port */ + const httpPort = envContent.match(/HTTP_PORT=(.+)/)?.[1] || '7777' + + const puppeteerConfig = { + logLevel: 'info', + headless: true, + debug: false, + browser: 'chrome', + maxPages: 15, + http: { + host: '0.0.0.0', + port: 7005, + token: '123456', + }, + ws: { + enable: true, + token: '123456', + path: '/ws', + list: [ + { + url: `ws://127.0.0.1:${httpPort}/puppeteer`, + token: wsAuthKey, + }, + ], + }, + browserCount: 1, + args: [ + '--enable-gpu', + '--no-sandbox', + '--disable-setuid-sandbox', + '--no-zygote', + '--disable-extensions', + '--disable-dev-shm-usage', + ], + } + + fs.mkdirSync(puppeteerDir, { recursive: true }) + fs.writeFileSync(path.join(puppeteerDir, 'config.json'), JSON.stringify(puppeteerConfig, null, 2)) + + console.log('📦 正在安装puppeteer...') + const cmd = `pnpm init && pnpm install @karinjs/puppeteer${getSuffix(isNpmMirror)} && npx init` + execSync(cmd, { + cwd: puppeteerDir, + stdio: 'inherit', + }) + + /** 写入app启动命令 */ + const pkg = fs.readFileSync(path.join(puppeteerDir, 'package.json'), 'utf-8') + const data = JSON.parse(pkg) + data.scripts.app = 'node index.js' + data.scripts.pm2 = 'k pm2' + data.scripts.stop = 'k stop' + data.scripts.rs = 'k rs' + data.scripts.log = 'k log' + fs.writeFileSync(path.join(puppeteerDir, 'package.json'), JSON.stringify(data, null, 2)) + console.log(green('✨ puppeteer 安装成功')) +} + +/** + * 检查网络环境 + */ +export const checkNetwork = async () => { + const networkResult = await pingUrls() + if (networkResult.ping) { + console.log(green('网络环境极佳 ^_^')) + return false + } else { + console.log(red('网络环境较差 将使用镜像源安装依赖~')) + return true + } +} + +/** + * 安装生产环境模板 + * @param name - 项目名称 + * @param puppeteer - 是否安装Puppeteer + */ +export const production = async (name: string, puppeteer: boolean) => { + try { + const isNpmMirror = await checkNetwork() + await createProjectStructure(name, isNpmMirror) + if (puppeteer) { + await installPuppeteer(name, isNpmMirror) + } + + const list = [ + '\n\n--------------------------------', + '\n✨ 项目创建成功!', + yellow('👇 请执行以下命令:\n'), + green(` cd ${name}`), + green(' pnpm app\n'), + ' 快捷指令(上下任选其一):', + magenta(` cd ${name} && pnpm app\n`), + '🚀 开始愉快的使用吧!', + ] + + if (puppeteer) { + list.push( + '--------------------------------', + ' 如需使用Puppeteer,请执行以下命令:', + green(` cd ${name}-puppeteer && pnpm app`), + '--------------------------------', + ) + } + + console.log(list.join('\n')) + } catch (error) { + console.log(magenta('Github: https://github.com/Karinjs/Karin/issues')) + console.log(red('发生错误,请将以下信息反馈给开发者:')) + console.log(green('--------------------------------')) + console.log(error) + console.log(green('--------------------------------')) + } +} diff --git a/packages/create-karin/src/utils/exec.ts b/packages/create-karin/src/utils/exec.ts new file mode 100644 index 0000000..2e055dc --- /dev/null +++ b/packages/create-karin/src/utils/exec.ts @@ -0,0 +1,79 @@ +import { exec as execCmd, execSync as execSyncCmd } from 'node:child_process' + +const shell = process.platform === 'win32' ? 'cmd' : '/bin/sh' + +/** + * 执行命令 + * @param cmd - 命令 + * @param options - 选项 + */ +export const execSync = ( + cmd: string, + options: import('node:child_process').ExecSyncOptions = {}, +): { status: boolean; error: Error | null; stdout: string; stderr: string } => { + try { + const result = execSyncCmd(cmd, { ...options, shell }) + return { status: true, error: null, stdout: result.toString(), stderr: '' } + } catch (error) { + return { status: false, error: error as Error, stdout: '', stderr: '' } + } +} + +/** + * 执行命令 + * @param cmd - 命令 + * @param options - 选项 + */ +export const exec = ( + cmd: string, + options: import('node:child_process').ExecOptions = {}, +): Promise<{ status: boolean; error: Error | null; stdout: string; stderr: string }> => { + return new Promise(resolve => { + execCmd(cmd, { ...options, shell }, (error, stdout, stderr) => { + const status = !error + resolve({ status, error, stdout, stderr }) + }) + }) +} + +/** + * 安装依赖 + * @param cwd - 工作目录 + * @param packageManager - 包管理器 + */ +export const installDependencies = async (cwd: string, packageManager = 'pnpm') => { + const commands = { + pnpm: 'pnpm install', + npm: 'npm install', + yarn: 'yarn install', + } as const + + const installCommand = commands[packageManager as keyof typeof commands] || commands.pnpm + + execSync(installCommand, { + cwd, + stdio: 'inherit', + shell, + }) +} + +/** + * 初始化Karin项目 + * @param targetDir - 目标目录 + */ +export const initKarinProject = async (targetDir: string) => { + console.log('targetDir: ', targetDir) + execSync('pnpm install node-karin@latest && npx karin init ', { + cwd: targetDir, + stdio: 'inherit', + }) +} + +/** + * 配置Puppeteer环境 + * @param puppeteerDir - Puppeteer目录 + */ +export const setupPuppeteer = async (puppeteerDir: string) => { + execSync('pnpm init && pnpm install @karinjs/puppeteer', { cwd: puppeteerDir, stdio: 'inherit' }) + execSync('npx init', { cwd: puppeteerDir, stdio: 'inherit' }) +} diff --git a/packages/create-karin/src/tasks.ts b/packages/create-karin/src/utils/tasks.ts similarity index 61% rename from packages/create-karin/src/tasks.ts rename to packages/create-karin/src/utils/tasks.ts index 9e98bef..60137dd 100644 --- a/packages/create-karin/src/tasks.ts +++ b/packages/create-karin/src/utils/tasks.ts @@ -49,3 +49,30 @@ export const installPm2 = async (suffix: string) => { throw new Error(red('pm2 安装失败 请检查你的网络环境')) } } + +/** + * 检查包管理器是否存在 + * @param packageManager - 包管理器名称 + */ +export const checkPackageManager = async (packageManager: string) => { + try { + const { status } = await exec(`${packageManager} --version`) + return status + } catch { + return false + } +} + +/** + * 安装包管理器 + * @param packageManager - 包管理器名称 + * @param suffix - 镜像源后缀 + */ +export const installPackageManager = async (packageManager: string, suffix = '') => { + if (packageManager === 'pnpm') { + await installPnpm(suffix) + } else if (packageManager === 'yarn') { + await exec(`npm install -g yarn${suffix}`) + } + // npm 不需要安装,因为它是 Node.js 自带的 +} diff --git a/packages/create-karin/src/tools.ts b/packages/create-karin/src/utils/tools.ts similarity index 89% rename from packages/create-karin/src/tools.ts rename to packages/create-karin/src/utils/tools.ts index 0e35e52..a9feb1b 100644 --- a/packages/create-karin/src/tools.ts +++ b/packages/create-karin/src/utils/tools.ts @@ -44,10 +44,10 @@ const DEFAULT_URLS = [ * @returns Promise */ export const ping = (url: string): Promise => { - return new Promise((resolve) => { + return new Promise(resolve => { const startTime = Date.now() - const req = https.get(url, (res) => { + const req = https.get(url, res => { const time = Date.now() - startTime resolve({ url, success: true, status: res.statusCode, time }) res.destroy() @@ -74,8 +74,8 @@ export const ping = (url: string): Promise => { const pingWithTimeout = (url: string, timeout: number): Promise => { return Promise.race([ ping(url), - new Promise((resolve) => - setTimeout(() => resolve({ url, success: false, time: timeout }), timeout) + new Promise(resolve => + setTimeout(() => resolve({ url, success: false, time: timeout }), timeout), ), ]) } @@ -97,15 +97,13 @@ const formatResults = (results: TestResult[]) => { */ export const testUrls = async ( urls: string[] = DEFAULT_URLS, - options: TestOptions = {} + options: TestOptions = {}, ): Promise<{ results: TestResult[]; succ: number; fail: number; count: number }> => { const { timeout = DEFAULT_TIMEOUT, silent = false } = options console.log(green(`开始测试网络连接... (超时时间: ${timeout}ms)`)) - const results = await Promise.all( - urls.map((url) => pingWithTimeout(url, timeout)) - ) + const results = await Promise.all(urls.map(url => pingWithTimeout(url, timeout))) if (silent) { formatResults(results) @@ -113,7 +111,7 @@ export const testUrls = async ( /** 统计测试数量 成功数量 */ let succ = 0 - results.forEach((result) => { + results.forEach(result => { if (result.success) succ++ }) @@ -124,13 +122,16 @@ export const testUrls = async ( * 执行命令 * @param command - 要执行的命令 */ -export const exec = (command: string, options: ExecOptions = {}): Promise<{ +export const exec = ( + command: string, + options: ExecOptions = {}, +): Promise<{ status: boolean error?: Error | null stdout?: string stderr?: string }> => { - return new Promise((resolve) => { + return new Promise(resolve => { execCommand(command, options, (error, stdout, stderr) => { const status = !error resolve({ status, error, stdout, stderr }) diff --git a/packages/create-karin/templates/js-plugin/.env b/packages/create-karin/templates/js-plugin/.env new file mode 100644 index 0000000..4372177 --- /dev/null +++ b/packages/create-karin/templates/js-plugin/.env @@ -0,0 +1,39 @@ +# 是否启用HTTP +HTTP_ENABLE=true +# HTTP监听端口 +HTTP_PORT=7777 +# HTTP监听地址 +HTTP_HOST=0.0.0.0 +# HTTP鉴权秘钥 仅用于karin自身Api +HTTP_AUTH_KEY=QtYTbI +# ws_server鉴权秘钥 +WS_SERVER_AUTH_KEY=QOnAho + + +# 是否启用Redis 关闭后将使用内部虚拟Redis +REDIS_ENABLE=true +# 重启是否调用pm2 如果不调用则会直接关机 此配置适合有进程守护的程序 +PM2_RESTART=true + + +# 日志等级 +LOG_LEVEL=info +# 日志保留天数 +LOG_DAYS_TO_KEEP=7 +# 日志文件最大大小 如果此项大于0则启用日志分割 +LOG_MAX_LOG_SIZE=0 +# logger.fnc颜色 +LOG_FNC_COLOR="#E1D919" + + +# ffmpeg +FFMPEG_PATH= +# ffprobe +FFPROBE_PATH= +# ffplay +FFPLAY_PATH= + + +# 这里请勿修改 +RUNTIME=node +NODE_ENV=development \ No newline at end of file diff --git a/packages/create-karin/templates/js-plugin/CHANGELOG.md b/packages/create-karin/templates/js-plugin/CHANGELOG.md deleted file mode 100644 index d121d03..0000000 --- a/packages/create-karin/templates/js-plugin/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# Changelog - -## [1.0.1](https://github.com/KarinJS/karin-plugin-template/compare/v1.0.0...v1.0.1) (2024-07-20) - - -### Bug Fixes - -* 测试2 ([b6a0b8d](https://github.com/KarinJS/karin-plugin-template/commit/b6a0b8d0f9175916ed6fbb3c034b4ee30f168c07)) - -## 1.0.0 (2024-07-20) - - -### Features - -* 添加更多示例插件 ([a850ac5](https://github.com/KarinJS/karin-plugin-template/commit/a850ac5fb1a03e8134f0fb2e517464e8117d5565)) - - -### Bug Fixes - -* 测试 ([94b2995](https://github.com/KarinJS/karin-plugin-template/commit/94b29953a99d75e31e548e031598eeaf8c17fa96)) -* 跟随上游 ([61d3bdf](https://github.com/KarinJS/karin-plugin-template/commit/61d3bdfc29801edaed046f35c771e68abe5540b7)) -* 路径构建 ([b17f344](https://github.com/KarinJS/karin-plugin-template/commit/b17f344db0cf6f3dfa29f6d0f903825b9c769e9f)) diff --git a/packages/create-karin/templates/js-plugin/eslint.config.js b/packages/create-karin/templates/js-plugin/eslint.config.mjs similarity index 100% rename from packages/create-karin/templates/js-plugin/eslint.config.js rename to packages/create-karin/templates/js-plugin/eslint.config.mjs diff --git a/packages/create-karin/templates/js-plugin/package.json b/packages/create-karin/templates/js-plugin/package.json index 2ce86c5..46b6ed3 100644 --- a/packages/create-karin/templates/js-plugin/package.json +++ b/packages/create-karin/templates/js-plugin/package.json @@ -14,16 +14,14 @@ "type": "module", "main": "lib/index.js", "scripts": { - "app": "karin start", - "dev": "karin dev", - "init": "karin init", - "start": "karin start" + "app": "karin app", + "dev": "karin dev" }, "devDependencies": { "neostandard": "^0.12.0" }, "peerDependencies": { - "node-karin": "0.12.2-5.pr.206.fd03f9f" + "node-karin": "latest" }, "karin": { "apps": [ @@ -44,5 +42,8 @@ "lib/**/*", "@karinjs/**/*" ] + }, + "dependencies": { + "node-karin": "1.1.1" } -} \ No newline at end of file +} diff --git a/packages/create-karin/templates/ts-plugin/.env b/packages/create-karin/templates/ts-plugin/.env new file mode 100644 index 0000000..c026fae --- /dev/null +++ b/packages/create-karin/templates/ts-plugin/.env @@ -0,0 +1,39 @@ +# 是否启用HTTP +HTTP_ENABLE=true +# HTTP监听端口 +HTTP_PORT=7777 +# HTTP监听地址 +HTTP_HOST=0.0.0.0 +# HTTP鉴权秘钥 仅用于karin自身Api +HTTP_AUTH_KEY=QtYTbI +# ws_server鉴权秘钥 +WS_SERVER_AUTH_KEY=QOnAho + + +# 是否启用Redis 关闭后将使用内部虚拟Redis +REDIS_ENABLE=true +# 重启是否调用pm2 如果不调用则会直接关机 此配置适合有进程守护的程序 +PM2_RESTART=true + + +# 日志等级 +LOG_LEVEL=info +# 日志保留天数 +LOG_DAYS_TO_KEEP=7 +# 日志文件最大大小 如果此项大于0则启用日志分割 +LOG_MAX_LOG_SIZE=0 +# logger.fnc颜色 +LOG_FNC_COLOR="#E1D919" + + +# ffmpeg +FFMPEG_PATH= +# ffprobe +FFPROBE_PATH= +# ffplay +FFPLAY_PATH= + + +# 这里请勿修改 +RUNTIME=tsx +NODE_ENV=development \ No newline at end of file diff --git a/packages/create-karin/templates/ts-plugin/eslint.config.js b/packages/create-karin/templates/ts-plugin/eslint.config.mjs similarity index 100% rename from packages/create-karin/templates/ts-plugin/eslint.config.js rename to packages/create-karin/templates/ts-plugin/eslint.config.mjs diff --git a/packages/create-karin/templates/ts-plugin/package.json b/packages/create-karin/templates/ts-plugin/package.json index 4dacde7..fdc926a 100644 --- a/packages/create-karin/templates/ts-plugin/package.json +++ b/packages/create-karin/templates/ts-plugin/package.json @@ -15,21 +15,14 @@ "scripts": { "build": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json", "pub": "npm publish --access public", - "karin": "karin", - "app": "karin app", - "start": "karin start", - "pm2": "karin pm2", - "stop": "karin stop", - "rs": "karin rs", - "log": "karin log", - "up": "karin up" + "dev": "tsx watch --include \"src/**/*.ts\" src/index.ts" }, "main": "lib/index.js", "devDependencies": { "@types/node": "^20.17.8", "eslint": "^9.7.0", "neostandard": "^0.11.9", - "node-karin": "0.12.25.pr.206.62c1986", + "node-karin": "latest", "tsc-alias": "^1.8.10", "tsx": "^4.19.2", "typescript": "^5.5.3" @@ -64,4 +57,4 @@ "access": "public", "registry": "https://registry.npmjs.org" } -} \ No newline at end of file +}