前端脚手架,自动创建远程仓库并推送

news2024/11/13 15:17:32

包含命令行选择和输入配置,远程仓库拉取模板,根据配置将代码注入模板框架的代码中,自动创建远程仓库,初始化git并提交至远程仓库,方便项目开发,简化流程。

目录结构

b29f2b9701c546a9b69285873f24d60e.png

创建一个bin文件夹,添加index.js文件,在这个文件中写下#! /usr/bin/env node

在package.json文件夹下

8172e131316244b0a33e2513d977e14e.png

执行 npm link 命令,链接到本地环境中 npm link (只有本地开发需要执行这一步,正常脚手架全局安装无需执行此步骤)Link 相当于将当前本地模块链接到npm目录下,这个目录可以直接访问,所以当前包就能直接访问了。默认package.json的name为基准,也可以通过bin配置别名。link完后,npm会自动帮忙生成命令,之后可以直接执行cli xxx。

直接上代码bin/index.js

#!/usr/bin/env node

import { Command } from 'commander';
import chalk from 'chalk';
import figlet from 'figlet';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import Creater from '../lib/create.js';
import { getProjectName } from '../utils/index.js';  // 引入封装好的模块
import '../utils/utils.js'
// 解析 __dirname 和 __filename
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// 异步读取 JSON 文件并解析
const packageJsonPath = path.resolve(__dirname, '../package.json');
const config = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));

const program = new Command();

// 欢迎信息
console.log(chalk.green(figlet.textSync('Ti CLI', {
    horizontalLayout: 'full'
})));

program
    .command('create')
    .description('create a new project')
    .option('-f, --force', 'overwrite target directory if it exists')
    .action(async (options) => {
        // 获取工作目录
        const cwd = process.cwd();

        // 提示用户输入项目名称
        const projectName = await getProjectName(cwd);

        // 目标目录也就是要创建的目录
        const targetDir = path.join(cwd, projectName);
        const creater = new Creater(projectName, targetDir,'git密令');
        try {
            await creater.create();
        } catch (error) {
            console.error(chalk.red(`创建项目失败: ${error.message}`));
        }
    });

program
    .command('build')
    .description('build the project')
    .action(() => {
        console.log('执行 build 命令');
        // 在这里实现 build 命令的具体逻辑
    });

program
    .command('serve')
    .description('serve the project')
    .option('-p, --port <port>', 'specify port to use', '3000')
    .action((options) => {
        console.log(`Serving on port ${options.port}`);
        // 在这里实现 serve 命令的具体逻辑
    });

program.on('--help', () => {
    console.log();
    console.log(`Run ${chalk.cyan('thingjs-ti <command> --help')} to show detail of this command`);
    console.log();
});

program
    .version(`thingjs-ti-cli@${config.version}`)
    .usage('<command> [options]');

program.parse(process.argv);

git密令那块儿填写自己的。

lib/create.js

import inquirer from 'inquirer';
import { exec } from 'child_process';
import { promisify } from 'util';
import { rm, cp } from 'fs/promises'; // 使用 fs/promises 模块
import { injectMainCode } from './injectCode.js';
import { fileURLToPath } from 'url'; // 引入 fileURLToPath
import axios from 'axios';
import path from 'path'; // 引入 path 模块

const execPromise = promisify(exec);
// 使用 import.meta.url 获取当前模块的路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

class Creater {
    constructor(projectName, targetDir, gitlabToken) {
        this.name = projectName;
        this.dir = targetDir;
        this.gitlabToken = gitlabToken;
        this.parentGroupId = '你的群组id';
        this.subGroupId = null;
        this.options = null;
        this.isOnline = true;
    }

    async create() {
        try {
            this.isOnline = await this.chooseDownloadMode();
            const template = await this.chooseTemplate();
            if (template === '园区项目基础框架') {
                this.options = await this.chooseOptions();
                if (this.isOnline) {
                    await this.downloadTemplate(template, this.options);
                } else {
                    await this.copyTemplate(template, this.options);
                }
                console.log('项目创建完成');
            } else {
                if (this.isOnline) {
                    await this.downloadTemplate(template);
                } else {
                    await this.copyTemplate(template);
                }
                console.log('项目创建完成');
            }

            if (this.isOnline) {
                // 创建子群组
                await this.createSubGroup(this.options.projectFullName);
                // 在子群组中创建远程仓库
                await this.createRemoteRepository();
            }
        } catch (error) {
            console.error('创建项目失败:', error);
        }
    }

    async createSubGroup(subgroupName) {
        if (!this.gitlabToken) {
            throw new Error('GitLab Token 未设置,请确保已设置环境变量 GITLAB_TOKEN');
        }

        try {
            const response = await axios.post(
                `git地址`,
                {
                    name: subgroupName,
                    path: this.name,
                    parent_id: this.parentGroupId,
                    visibility: 'private' // 可以选择 'private' 或 'public'
                },
                {
                    headers: {
                        'Authorization': `Bearer ${this.gitlabToken}`,
                        'Content-Type': 'application/json'
                    }
                }
            );
            this.subGroupId = response.data.id;
            console.log(`子群组创建成功: ${subgroupName}`);
        } catch (error) {
            console.error('创建子群组失败:', error);
            throw error;
        }
    }

    async createRemoteRepository() {
        if (!this.gitlabToken) {
            throw new Error('GitLab Token 未设置,请确保已设置环境变量 GITLAB_TOKEN');
        }

        if (!this.subGroupId) {
            throw new Error('子群组 ID 未设置,请确保子群组已创建');
        }

        try {
            const response = await axios.post(
                'git地址',
                {
                    name: `${this.name}-web`,
                    namespace_id: this.subGroupId,
                    visibility: 'private' // 可以选择 'private' 或 'public'
                },
                {
                    headers: {
                        'Authorization': `Bearer ${this.gitlabToken}`,
                        'Content-Type': 'application/json'
                    }
                }
            );
            const repoUrl = response.data.ssh_url_to_repo;
            console.log(`远程仓库创建成功: ${repoUrl}`);

            // 初始化本地 Git 仓库并创建初始提交
            await execPromise(`cd ${this.dir} && git init`);
            await execPromise(`cd ${this.dir} && git add .`);
            await execPromise(`cd ${this.dir} && git commit -m "Initial commit"`);

            // 添加远程仓库并推送初始提交
            await execPromise(`cd ${this.dir} && git remote add origin ${repoUrl}`);
            await execPromise(`cd ${this.dir} && git push -u origin main`);

        } catch (error) {
            console.error('创建远程仓库失败:', error);
            throw error;
        }
    }

    async chooseOptions() {
        // 用户输入项目全称
        const { projectFullName } = await inquirer.prompt({
            type: 'input',
            name: 'projectFullName',
            message: '请输入项目全称(git群组名称):',
            validate: (input) => input ? true : '项目全称不能为空'
        });
        return {projectFullName };
    }

    async chooseTemplate() {
        const answers = await inquirer.prompt({
            type: 'list',
            name: 'template',
            message: '请选择项目模板:',
            choices: ['园区标准项目模板', '园区项目基础框架'],
            default: '园区项目基础框架'
        });
        return answers.template;
    }

    async chooseDownloadMode() {
        const answers = await inquirer.prompt({
            type: 'list',
            name: 'downloadMode',
            message: '请选择下载模式:',
            choices: ['在线', '离线'],
            default: '离线'
        });
        return answers.downloadMode === '在线';
    }

    async handleOptions(options) {
        await injectMainCode(this.dir, { needLogin: options.needLogin, width: options.width, height: options.height });
    }

    async downloadTemplate(template, options) {
        const repoUrls = {
            '园区标准项目模板': '模板git地址',
            '园区项目基础框架': '模板git地址'
        };

        const repoUrl = repoUrls[template];
        if (!repoUrl) {
            throw new Error(`未知的模板: ${template}`);
        }

        try {
            console.log(`正在下载模板: ${repoUrl}`);
            await execPromise(`git clone ${repoUrl} ${this.dir}`);

            // 删除 .git 文件夹
            await rm(`${this.dir}/.git`, { recursive: true, force: true });

            if (template === '园区项目基础框架') {
                await this.handleOptions(options);
            }
        } catch (error) {
            console.error('模板下载失败:', error);
            throw error;
        }
    }

    async copyTemplate(template, options) {
        const templates = {
            '园区标准项目模板': path.resolve(__dirname, '../framework/ti-campus-template'),
            '园区项目基础框架': path.resolve(__dirname, '../framework/ti-project-template')
        };

        const templatePath = templates[template];
        if (!templatePath) {
            throw new Error(`未知的模板: ${template}`);
        }

        try {
            console.log(`正在复制模板: ${templatePath}`);
            await cp(templatePath, this.dir, { recursive: true });

            if (template === '园区项目基础框架') {
                await this.handleOptions(options);
            }
        } catch (error) {
            console.error('模板复制失败:', error);
            throw error;
        }
    }
}

export default Creater;

注意替换git地址,请求的git接口可以自己看看gitlab文档,parentGroupId是指群组id,各个公司不一样的,可以根据自己的来。

lib/injectCode.js这个主要是向模板框架中注入配置项代码

import path from 'path';
import fs from 'fs/promises';
import prettier from 'prettier';

async function injectMainCode(targetDir, options) {
    const mainTsPath = path.join(targetDir, 'src', 'main.ts');
    let loginCode ='';
    if(!options.needLogin){
        loginCode =‘你的代码’
    }else{
        loginCode = `你的代码`    
    }
    try {
        // 清空 main.ts 文件内容
        await fs.writeFile(mainTsPath, '', 'utf-8');
        // 读取 main.ts 文件
        let mainTsContent = await fs.readFile(mainTsPath, 'utf-8');

        if (!mainTsContent.includes('initLogin()')) {
            mainTsContent += loginCode;

            // 使用 Prettier 格式化代码
            const formattedContent = await prettier.format(mainTsContent, { parser: 'typescript' });

            await fs.writeFile(mainTsPath, formattedContent, 'utf-8');
            console.log('已向 main.ts 中注入登录功能代码');
        }
    } catch (error) {
        console.error('更新 main.ts 失败:', error);
        throw error;
    }
}

export { injectMainCode };

utils/utils.js这里封装了一个工具函数

// 自定义 findLastIndex 函数
if (!Array.prototype.findLastIndex) {
    Array.prototype.findLastIndex = function (predicate, thisArg) {
        for (let i = this.length - 1; i >= 0; i--) {
            if (predicate.call(thisArg, this[i], i, this)) {
                return i;
            }
        }
        return -1;
    };
}

好了这就结束了,这就是一个很基础很简单的脚手架,可能日常工作需要更复杂更全面的,可以继续在上面叠加,我相信你看完起码觉的这也没什么难的了。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101964.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

KAN 学习 Day2 —— utils.py及spline.py 代码解读及测试

在KAN学习Day1——模型框架解析及HelloKAN中&#xff0c;我对KAN模型的基本原理进行了简单说明&#xff0c;并将作者团队给出的入门教程hellokan跑了一遍&#xff0c;今天我们直接开始进行源码解读。 目录 一、kan目录 二、utils.py 2.1 导入库和模块 2.2 逆函数定义 2.3 …

CentOS 7安装Docker详细步骤-无坑-丝滑-顺畅

一&#xff0c;安装软件包 yum install -y yum-utils device-mapper-persistent-data lvm2二&#xff0c;更换yum源为阿里源&#xff1a; yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 三&#xff0c;查看docker版本&…

标准库标头 <optional> (C++17)学习之optional

类模板 std::optional 管理一个可选 &#xfeff;的所含值&#xff0c;即既可以存在也可以不存在的值。 一种常见的 optional 使用情况是作为可能失败的函数的返回值。与如 std::pair<T, bool> 等其他手段相比&#xff0c;optional 可以很好地处理构造开销高昂的对象&a…

【科普】双轴测径仪是根据哪个测量值控制外径尺寸?

单轴测径仪与双轴测径仪都是自带闭环控制功能的在线外径测量设备&#xff0c;单轴测径仪只有一个测头&#xff0c;是根据该测头的检测数据进行控制&#xff0c;这点毋庸置疑&#xff0c;那双轴测径仪这种具备两组测头的设备又是如何控制的&#xff0c;本文就来简单的介绍一下。…

Ubuntu安装网卡驱动

没有无线网 给自己装了双系统后&#xff0c;发现没有无线网络 下载驱动文件 打开终端&#xff0c;输入 lspci -k 能看到&#xff0c;虽然我是RTL8125BG&#xff0c;但use的是r8169: 08:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controll…

Vue前端路由详解——以Ruoyi框架为案例学习

Vue路由 Vue路由详解_vue 页面路由-CSDN博客 路由模式 Vue 的路由模式&#xff1a;hash 模式和 history 模式的区别_vue路由history和hash的区别-CSDN博客 URL格式&#xff1a; Hash模式&#xff1a;URL中包含#号&#xff0c;用于区分页面部分&#xff0c;实际请求的页面地址…

OpenCV下的无标定校正(stereoRectifyUncalibrated)

OpenCV下的无标定校正(stereoRectifyUncalibrated) 文章目录 1. 杂话2. 无标定校正2.1 先看代码2.2 一点解释2.3 findFundamentalMat参数2.4 stereoRectifyUncalibrated参数 3. 矫正结果 1. 杂话 咱们在之前的帖子里面讲了一些比较常规的标定和校正OpenCV下的单目标定&#xff…

Unity数据持久化 之 文件操作(增删查改)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​​ 这里需要弄清几个概念&#xff1a; File&#xff1a;提供文件操作的静态方法&#xff0c;是管理的 Windows.File -…

除浮毛用吸尘器有用吗?除浮毛真正有用浮毛空气净化器总结

我的医生朋友经常给朋友们讲解宠物毛发对呼吸道的潜在影响&#xff0c;这引起了不同的反应。有人采纳了他的建议&#xff0c;采取了防护措施&#xff1b;而有人则认为他在制造恐慌&#xff0c;特别是当听到宠物医生的说法与之相左时。 我曾也心存疑虑&#xff0c;但经过与朋友…

做开发一年多了,分享一下自己的疑惑以及大模型给我的一些建议~

写在最前面,下面的疑问是我自己的一些困惑和想知道背后的答案,回答这块是大模型的一些建议,我觉得对我来说不能说很对,至少给我了启发和思考,分享出来给大家,大家如果也有类似的疑惑,希望能提供到帮助 原先Java生态是出现各种复杂的业务场景,需要使用合理且合适的技术架…

house of cat

文章目录 house of cat概述&#xff1a;_IO_wfile_jumps进入_IO_wfile_seekoffFSOP__malloc_assert 例题&#xff1a;思路&#xff1a;分析&#xff1a;利用&#xff1a; house of cat 概述&#xff1a; house of cat主要的摸底还是覆盖vtable指针&#xff0c;因为在glibc-2.2…

结构型设计模式—桥接模式

结构型设计模式—桥接模式 欢迎长按图片加好友&#xff0c;我会第一时间和你分享持续更多的开发知识&#xff0c;面试资源&#xff0c;学习方法等等。 假设你要买一张新桌子&#xff0c;你有两个选择&#xff1a;一种是木制的桌子&#xff0c;另一种是金属制的桌子。 无论你选…

软件工程知识点总结(1):软件工程概述

1 什么是软件&#xff1f; 定义&#xff1a;计算机系统中的程序及其文档。 ——程序是计算机任务的处理对象和处理规模的描述&#xff1b; ——文档是为了便于了解程序所需要的阐明性资料。 2 软件的特点&#xff1f; 软件是无形的&#xff0c;不可见的逻辑实体 ——它的正确与…

【Python基础】字典类型

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、Python 字典类型2.1 访问字典里的值2.2 修改字典2.3 删除字典元素2.4 字典键值的特性2.5 遍历字典…

免费pdf转word软件,为你整理出8种方法,总有一个适合你

在日常办公和学习中&#xff0c;PDF文档因其格式稳定、不易修改的特性而广受欢迎。然而&#xff0c;有时我们需要对PDF内容进行编辑或格式调整&#xff0c;这时将其转换为Word文档便显得尤为重要。下面给大家介绍8种将PDF转换成Word的方法&#xff0c;包括在线网站、专业软件及…

第四篇——数学思维:数学家如何从逻辑出发想问题?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 数学思维中的很多方法能够让我们脱离事物的表象去直击本质通过本质进行逻…

在Linux中使用MySQL基础SQL语句及校验规则

卸载内置环境 查看是否存在MySQL ps axj | grep mysql关闭MySQL systemctl stop mysqld MySQL对应的安装文件 rpm -qa | grep mysql 批量卸载 rpm -qa | grep mysql | xargs yum -y remove 上传MySQL rz 查看本地yum源 ls /etc/yum.repos.d/ -a 安装MySQL rpm -ivh…

Linux:手搓shell

之前学了一些和进程有关的特性&#xff0c;什么进程控制啊进程替换啊&#xff0c;我们来尝试自己搓一个shell()吧 首先我们观察shell的界面&#xff0c;发现centos的界面上有命令提示符&#xff1a; [主机名用户名当前路径] 我们可以通过调用系统函数获取当前路径&#xff0…

C语言代码练习(第十二天)

今日练习&#xff1a; 28、&#xff08;指针&#xff09;将字符串 a 复制为字符串 b &#xff0c;然后输出字符串 b 29、改变指针变量的值 30、输入两个整数&#xff0c;然后让用户选择1或者2&#xff0c;选择1是调用 max &#xff0c;输出两者中的大数&#xff0c;选择2是调用…

Mac M1 安装Hadoop教程(安装包安装)

一、引言 前面一期&#xff0c;我分享了通过homebrew方式安装Hadoop&#xff0c;本期我将通过安装包方式介绍下hadoop如何安装。二、下载open jdk8 官方下载地址 注意如果是x86架构的苹果电脑&#xff0c;Architecture选择x86 64-bit或者 x86-32bit。 下载后&#xff0c;将得…