From 9407d63be5a52bdaaf6e5fbb89283f813a969a0f Mon Sep 17 00:00:00 2001 From: bunny <1319900154@qq.com> Date: Fri, 22 Dec 2023 17:38:13 +0800 Subject: [PATCH] =?UTF-8?q?completepage:=20=E5=AE=8C=E6=88=90=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2:=20=F0=9F=8D=BB=20=E5=9F=BA=E6=9C=AC=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 22 +++++++- .prettierrc.js | 39 ++++++++++++++ bin/command-log.js | 29 ++++++++++ bin/config.js | 67 +++++++++++++++++++++++ bin/demo.js | 132 ++++++++++++++++++++++----------------------- bin/index.js | 39 +++++++++----- bin/utils.js | 84 +++++++++++++++++++++++++++++ package-lock.json | 6 +++ package.json | 89 +++++++++++++++--------------- 9 files changed, 384 insertions(+), 123 deletions(-) create mode 100644 .prettierrc.js create mode 100644 bin/command-log.js create mode 100644 bin/config.js create mode 100644 bin/utils.js diff --git a/.gitignore b/.gitignore index b512c09..7df6d18 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,21 @@ -node_modules \ No newline at end of file +.DS_Store +node_modules + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..25f94d1 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,39 @@ +// @see: https://www.prettier.cn + +module.exports = { + // 超过最大值换行 + printWidth: 130, + // 缩进字节数 + tabWidth: 2, + // 使用制表符而不是空格缩进行 + useTabs: true, + // 结尾不用分号(true有,false没有) + semi: true, + // 使用单引号(true单引号,false双引号) + singleQuote: true, + // 更改引用对象属性的时间 可选值"" + quoteProps: 'as-needed', + // 在对象,数组括号与文字之间加空格 "{ foo: bar }" + bracketSpacing: true, + // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none + trailingComma: 'all', + // 在JSX中使用单引号而不是双引号 + jsxSingleQuote: true, + // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号 + arrowParens: 'avoid', + // 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。 + insertPragma: false, + // 指定要使用的解析器,不需要写文件开头的 @prettier + requirePragma: false, + // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行 + proseWrap: 'preserve', + // 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的 + htmlWhitespaceSensitivity: 'css', + // 换行符使用 lf 结尾是 可选值"" + endOfLine: 'auto', + // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 + rangeStart: 0, + rangeEnd: Infinity, + + vueIndentScriptAndStyle: false, // Vue文件脚本和样式标签缩进 +}; diff --git a/bin/command-log.js b/bin/command-log.js new file mode 100644 index 0000000..985ad00 --- /dev/null +++ b/bin/command-log.js @@ -0,0 +1,29 @@ +const { program } = require('commander'); // 命令行提示工具 +const figlet = require('figlet'); // 生成艺术字 + +module.exports = { + /** + * 给help信息添加提示 + */ + CommandLogHelp() { + program.on('--help', function () { + console.log( + // 设置帮助信息下打印艺术字 + figlet.textSync('Bunny-Cli', { + font: 'Ghost', + horizontalLayout: 'default', + verticalLayout: 'default', + width: 100, + whitespaceBreak: true, + }), + ); + }); + }, + /** + * 给出version版本号 + */ + CommandLogStart() { + program.name('bunny-cli').usage(' [options]'); + program.version(`😉 v${require('../package.json').version}`); + }, +}; diff --git a/bin/config.js b/bin/config.js new file mode 100644 index 0000000..87585cd --- /dev/null +++ b/bin/config.js @@ -0,0 +1,67 @@ +// 选择框架和语言 +const frame = [ + { + type: 'list', + message: '选择什么框架去新建项目❓', + name: 'type', + choices: [ + { name: 'react', value: 'react' }, + { name: 'vue', value: 'vue' }, + ], + }, + { + type: 'list', + message: '是否使用TypeScript❓', + name: 'isTypeScript', + choices: [ + { name: '是', value: 'ts' }, + { name: '否', value: 'js' }, + ], + }, +]; + +// react框架 +const react = [ + { + type: 'list', + message: '📢react默认包含redux/toolkit;🚶选择路由创建位置', + name: 'router', + choices: [ + { name: '使用RouterProvider', value: 'RouterProvider' }, + { name: '使用userouter', value: 'userouter' }, + { name: '不使用路由', value: '' }, + ], + }, +]; + +// vue +const vue = [ + { + type: 'list', + message: '🚶选择vue仓库', + name: 'router', + choices: [ + { name: 'pina', value: 'pina' }, + { name: 'vuex', value: 'vuex' }, + ], + }, +]; + +// 项目地址链接 +module.exports = { + // 项目列表 + projectList: { + 'vue&js&vuex': '1', + 'vue&js&pina': '2', + 'vue&ts&vuex': '3', + 'vue&ts&pina': '4', + 'react&js&': '5', + 'react&ts&': '6', + 'react&js&userouter': '7', + 'react&ts&userouter': '8', + 'react&js&RouterProvider': '9', + 'react&ts&RouterProvider': '10', + }, + // 问题 + question: { frame, react, vue }, +}; diff --git a/bin/demo.js b/bin/demo.js index 0971f8d..d1df706 100644 --- a/bin/demo.js +++ b/bin/demo.js @@ -1,95 +1,95 @@ #!/usr/bin/env node -const { program } = require("commander"); // 命令行提示工具 -const chalk = require("chalk"); // 终端输出颜色变化 -const inquirer = require("inquirer"); // 交互选项 -const ora = require("ora"); // loading效果 -const spinner = ora("Loading unicorns").start(); // 注意要使用x几版本因为不支持 -const figlet = require("figlet"); // 生成艺术字 +const { program } = require('commander'); // 命令行提示工具 +const chalk = require('chalk'); // 终端输出颜色变化 +const inquirer = require('inquirer'); // 交互选项 +const ora = require('ora'); // loading效果 +const spinner = ora('Loading unicorns').start(); // 注意要使用x几版本因为不支持 +const figlet = require('figlet'); // 生成艺术字 -figlet("Hello World!!", function (err, data) { - if (err) { - console.log("Something went wrong..."); - console.dir(err); - return; - } - console.log(data); +figlet('Hello World!!', function (err, data) { + if (err) { + console.log('Something went wrong...'); + console.dir(err); + return; + } + console.log(data); }); figlet.text( - "Boo!", - { - font: "Ghost", - horizontalLayout: "default", - verticalLayout: "default", - width: 80, - whitespaceBreak: true, - }, - function (err, data) { - if (err) { - console.log("Something went wrong..."); - console.dir(err); - return; - } - console.log(data); - }, + 'Boo!', + { + font: 'Ghost', + horizontalLayout: 'default', + verticalLayout: 'default', + width: 80, + whitespaceBreak: true, + }, + function (err, data) { + if (err) { + console.log('Something went wrong...'); + console.dir(err); + return; + } + console.log(data); + }, ); console.log( - figlet.textSync("Boo!", { - font: "Ghost", - horizontalLayout: "default", - verticalLayout: "default", - width: 80, - whitespaceBreak: true, - }), + figlet.textSync('Boo!', { + font: 'Ghost', + horizontalLayout: 'default', + verticalLayout: 'default', + width: 80, + whitespaceBreak: true, + }), ); setTimeout(() => { - spinner.color = "red"; - spinner.text = "网络较慢,请稍后..."; + spinner.color = 'red'; + spinner.text = '网络较慢,请稍后...'; }, 1000); setTimeout(() => { - spinner.succeed("下载成功!!!"); + spinner.succeed('下载成功!!!'); }, 2000); setTimeout(() => { - spinner.fail("下载失败!!!"); + spinner.fail('下载失败!!!'); }, 3000); inquirer - .prompt([ - /* Pass your questions in here */ - { type: "input", name: "food", message: "你吃什么", default: "包子" }, - { type: "input", name: "food", message: "吃辣吗?", default: "微辣" }, - ]) - .then((answers) => { - // Use user feedback for... whatever!! - console.log(answers); - }) - .catch((error) => { - if (error.isTtyError) { - // Prompt couldn't be rendered in the current environment - } else { - // Something else went wrong - } - }); + .prompt([ + /* Pass your questions in here */ + { type: 'input', name: 'food', message: '你吃什么', default: '包子' }, + { type: 'input', name: 'food', message: '吃辣吗?', default: '微辣' }, + ]) + .then(answers => { + // Use user feedback for... whatever!! + console.log(answers); + }) + .catch(error => { + if (error.isTtyError) { + // Prompt couldn't be rendered in the current environment + } else { + // Something else went wrong + } + }); -console.log(chalk.red("文字颜色")); +console.log(chalk.red('文字颜色')); -program.name("bunny-cli").usage(" [options]"); +program.name('bunny-cli').usage(' [options]'); program - .option("-d, --debug", "output extra debugging") - .option("-s, --small", "small pizza size") - .option("-p, --pizza-type ", "flavour of pizza"); + .option('-d, --debug', 'output extra debugging') + .option('-s, --small', 'small pizza size') + .option('-p, --pizza-type ', 'flavour of pizza'); program - .command("clone [destination]") - .description("clone a repository into a newly created directory") - .action((source, destination) => { - console.log(source, destination); - }); + .command('clone [destination]') + .description('clone a repository into a newly created directory') + .action((source, destination) => { + console.log(source, destination); + }); program.parse(process.argv); diff --git a/bin/index.js b/bin/index.js index 9c93064..0c27ac2 100644 --- a/bin/index.js +++ b/bin/index.js @@ -1,14 +1,29 @@ #!/usr/bin/env node -const { program } = require("commander"); // 命令行提示工具 -const chalk = require("chalk"); // 终端输出颜色变化 -const inquirer = require("inquirer"); // 交互选项 -const ora = require("ora"); // loading效果 -const spinner = ora("Loading unicorns").start(); // 注意要使用x几版本因为不支持 -const figlet = require("figlet"); // 生成艺术字 +const { program } = require('commander'); // 命令行提示工具 +const path = require('path'); // 引入操作路径 +const { CommandLogHelp, CommandLogStart } = require('./command-log'); +const { fileIsExist, createProject, downloadProject } = require('./utils'); -console.log(program); -console.log(chalk); -console.log(inquirer); -console.log(ora); -console.log(spinner); -console.log(figlet); +// 首行显示 +CommandLogStart(); + +// 创建项目命令 +program + .command('create ') + .description('创建一个新项目') + .action(async function (name) { + // 创建项目-创建一个名字为name的项目,如果存在是否覆盖? + const targetPath = path.join(process.cwd(), name); + + // 文件夹下是否有这个名字 + await fileIsExist(targetPath, name); + + // 新建项目 + const key = await createProject(); + // clone项目 + downloadProject(targetPath, key, name); + }); + +// 给help信息添加提示 +CommandLogHelp(); +program.parse(process.argv); diff --git a/bin/utils.js b/bin/utils.js new file mode 100644 index 0000000..055e5dc --- /dev/null +++ b/bin/utils.js @@ -0,0 +1,84 @@ +const chalk = require('chalk'); // 终端输出颜色变化 +const inquirer = require('inquirer'); // 交互选项 +const ora = require('ora'); // loading效果 +const gitClone = require('git-clone/promise'); // 拉取项目 +const fs = require('fs-extra'); // 操作文件 +const path = require('path'); // 引入操作路径 +const { question, projectList } = require('./config'); + +module.exports = { + /** + * 判断文件是否存在 + * 是否需要覆盖 + */ + async fileIsExist(targetPath, name) { + console.log(targetPath); + if (fs.existsSync(targetPath, name)) { + const awsaner = await inquirer.prompt({ + type: 'confirm', + message: '是否覆盖文件夹❓', + default: false, + name: 'overWrite', + }); + + // 用户选择是则删除文件夹 + if (awsaner.overWrite) { + fs.remove(targetPath); + console.log(chalk.green('✅删除成功')); + } else return; + } + }, + + /** + * 创建文项目 + */ + async createProject() { + // 选择框架和语言 + const frame = await inquirer.prompt(question.frame); + const frame_isTypeScript = `${frame.type}&${frame.isTypeScript}`; + + // 如果选择的react框架 + if (frame.type === 'react') { + const awsaner = await inquirer.prompt(question.react); + const key = `${frame_isTypeScript}&${awsaner.router}`; + return projectList[key]; + } + // 如果选择是vue框架 + else { + const awsaner = await inquirer.prompt(question.vue); + const key = `${frame_isTypeScript}&${awsaner.router}`; + return projectList[key]; + } + }, + /** + * 从仓库clone项目 + */ + downloadProject(targetPath, key, name) { + const spinner = ora('下载中...').start(); // 注意要使用x几版本因为不支持 + + // 如果有git,删除原有的git + if (fs.existsSync(targetPath, '.git')) { + fs.remove(path.join(targetPath, '.git')); + } + + gitClone(projectList[key], name, { checkout: 'main' }, function (error) { + if (error) { + spinner.fail('😭下载失败,请稍后重试'); // 注意要使用x几版本因为不支持 + } else { + spinner.succeed('😄下载成功'); // 注意要使用x几版本因为不支持 + const overText = ` + ⚓ Running completion hooks... + + 📄 Generating README.md... + + 🎉 Successfully created project ${name} + 👉 Get started with the following commands: + + cd vue_js_vuex + npm run serve + `; + console.log(overText); + } + }); + }, +}; diff --git a/package-lock.json b/package-lock.json index f69842a..82f5005 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "eslint-plugin-prettier": "^5.1.1", "figlet": "^1.7.0", "fs-extra": "^11.2.0", + "git-clone": "^0.2.0", "inquirer": "^8.0.0", "ora": "^5.0.1", "prettier": "^3.1.1" @@ -2125,6 +2126,11 @@ "node": ">=16" } }, + "node_modules/git-clone": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/git-clone/-/git-clone-0.2.0.tgz", + "integrity": "sha512-1UAkEPIFbyjHaddljUKvPhhLRnrKaImT71T7rdvSvWLXw95nLdhdi6Qmlx0KOWoV1qqvHGLq5lMLJEZM0JXk8A==" + }, "node_modules/git-raw-commits": { "version": "2.0.11", "resolved": "https://registry.npmmirror.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz", diff --git a/package.json b/package.json index d19fca3..28e9446 100644 --- a/package.json +++ b/package.json @@ -1,46 +1,47 @@ { - "name": "bunny-cli", - "version": "1.0.0", - "description": "", - "main": "index.js", - "bin": { - "bunny-cli": "./bin/index.js" - }, - "scripts": { - "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", - "lint:prettier": "prettier --write --loglevel warn \"bin/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"", - "lint:lint-staged": "lint-staged", - "prepare": "husky install", - "release": "standard-version", - "commit": "git status && git add -A && git-cz", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "chalk": "^4.0.0", - "commander": "^11.1.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.1", - "figlet": "^1.7.0", - "fs-extra": "^11.2.0", - "inquirer": "^8.0.0", - "ora": "^5.0.1", - "prettier": "^3.1.1" - }, - "devDependencies": { - "@commitlint/cli": "^18.4.3", - "@commitlint/config-conventional": "^18.4.3", - "commitizen": "^4.3.0", - "cz-git": "^1.8.0", - "eslint": "^8.56.0", - "husky": "^8.0.3", - "lint-staged": "^15.2.0" - }, - "config": { - "commitizen": { - "path": "node_modules/cz-git" - } - } + "name": "bunny-cli", + "version": "1.0.0", + "description": "", + "main": "index.js", + "bin": { + "bunny-cli": "./bin/index.js" + }, + "scripts": { + "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", + "lint:prettier": "prettier --write --loglevel warn \"bin/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"", + "lint:lint-staged": "lint-staged", + "prepare": "husky install", + "release": "standard-version", + "commit": "git status && git add -A && git-cz", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "commander": "^11.1.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.1", + "figlet": "^1.7.0", + "fs-extra": "^11.2.0", + "git-clone": "^0.2.0", + "inquirer": "^8.0.0", + "ora": "^5.0.1", + "prettier": "^3.1.1" + }, + "devDependencies": { + "@commitlint/cli": "^18.4.3", + "@commitlint/config-conventional": "^18.4.3", + "commitizen": "^4.3.0", + "cz-git": "^1.8.0", + "eslint": "^8.56.0", + "husky": "^8.0.3", + "lint-staged": "^15.2.0" + }, + "config": { + "commitizen": { + "path": "node_modules/cz-git" + } + } }