├── .gitignore ├── lib ├── api.js ├── util.js ├── create.js └── creator.js ├── package.json ├── README.md └── bin └── enter.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /lib/api.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | 3 | // 拦截全局请求响应 4 | axios.interceptors.response.use((res) => { 5 | return res.data; 6 | }); 7 | 8 | /** 9 | * 获取模板 10 | * @returns Promise 仓库信息 11 | */ 12 | async function getMagicalRepo() { 13 | return axios.get("https://api.github.com/orgs/Magical-cli-v-1/repos"); 14 | } 15 | 16 | /** 17 | * 获取仓库下的版本 18 | * @param {string} repo 模板名称 19 | * @returns Promise 版本信息 20 | */ 21 | async function getTagsByRepo(repo) { 22 | return axios.get(`https://api.github.com/repos/Magical-cli-v-1/${repo}/tags`); 23 | } 24 | 25 | 26 | 27 | module.exports = { 28 | getMagicalRepo, 29 | getTagsByRepo, 30 | }; -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | const ora = require("ora"); 2 | 3 | /** 4 | * 睡觉函数 5 | * @param {Number} n 睡眠时间 6 | */ 7 | function sleep(n) { 8 | return new Promise((resolve, reject) => { 9 | setTimeout(() => { 10 | resolve(); 11 | }, n); 12 | }); 13 | } 14 | 15 | /** 16 | * loading加载效果 17 | * @param {String} message 加载信息 18 | * @param {Function} fn 加载函数 19 | * @param {List} args fn 函数执行的参数 20 | * @returns 异步调用返回值 21 | */ 22 | async function loading(message, fn, ...args) { 23 | const spinner = ora(message); 24 | spinner.start(); // 开启加载 25 | try { 26 | let executeRes = await fn(...args); 27 | // 加载成功 28 | spinner.succeed(); 29 | return executeRes; 30 | } catch (error) { 31 | // 加载失败 32 | spinner.fail("request fail, reTrying"); 33 | await sleep(1000); 34 | // 重新拉取 35 | return loading(message, fn, ...args); 36 | } 37 | } 38 | 39 | module.exports = { 40 | loading, 41 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "magical-cli-v50", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "magical": "./bin/enter.js", 8 | "magical-cli": "./bin/enter.js", 9 | "mg": "./bin/enter.js", 10 | "mg-cli": "./bin/enter.js" 11 | }, 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "files": [ 16 | "bin", 17 | "lib" 18 | ], 19 | "keywords": [ 20 | "cli", 21 | "magical-cli", 22 | "electron", 23 | "electron-cli", 24 | "react", 25 | "react-cli", 26 | "脚手架" 27 | ], 28 | "author": { 29 | "name": "magicalboys", 30 | "email": "magical20021124@gmail.com" 31 | }, 32 | "license": "ISC", 33 | "dependencies": { 34 | "axios": "^1.4.0", 35 | "chalk": "^4.0.0", 36 | "commander": "^10.0.1", 37 | "download-git-repo": "^3.0.2", 38 | "figlet": "^1.6.0", 39 | "fs-extra": "^11.1.1", 40 | "inquirer": "^8.0.0", 41 | "ora": "^3.4.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magical-CLI-V50 2 | 3 | **👉模板仓库地址:[magical-cli-v50](https://github.com/orgs/Magical-cli-v-1/repositories)** 4 | 5 | ## 全局安装 6 | 7 | `npm i -g magical-cli-v50` 8 | 9 | ## 常用命令 10 | 11 | * `mg / magcial create `: 创建项目 12 | 13 | * For example: 14 | 15 | * `mg create app` 16 | 17 | * `magcial create app` 18 | 19 | ## 选择模板 20 | 21 | ## [`election-template`](https://github.com/Magical-cli-v-1/electron-template-v50) 22 | 23 | ### 傻瓜式配置,将你的 web 页面秒变 PC 端应用 🤺 24 | 25 | ### 配置 26 | 27 | * `cnpm install` ,注意,这里最好用 `cnpm` 28 | 29 | * 在 `app/main/index.js` 文件夹找到 `http://localhost:3000/` 直接替换为你自己项目的 web页面 `URL` 30 | ``` 31 | // 创建一个变量来保存主窗口实例 32 | let win; 33 | 34 | // TODO: 创建主窗口 35 | const createWindow = () => { 36 | 37 | // TODO: 需要嵌入的web页面的 URL 38 | win.loadURL('http://localhost:3000/') 39 | 40 | } 41 | ``` 42 | 43 | ### 启动 44 | 45 | * 先在自己的项目执行 `npm run start`(如果是已经部署的就不需要了) 46 | 47 | * 然后在此项目执行 `npm run start` 48 | 49 | ### 打包 50 | 51 | * `npm run park` 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /lib/create.js: -------------------------------------------------------------------------------- 1 | // 当前函数中可能存在很多异步操作,因此我们将其包装为 async 2 | const path = require("path"); 3 | const fs = require("fs-extra"); 4 | const Inquirer = require("inquirer"); 5 | const Creator = require("./creator"); 6 | 7 | module.exports = async function (projectName, options) { 8 | // 获取当前工作目录 9 | const cwd = process.cwd(); 10 | // 拼接得到项目目录 11 | const targetDirectory = path.join(cwd, projectName); 12 | // 判断目录是否存在 13 | if (fs.existsSync(targetDirectory)) { 14 | // 判断是否使用 --force 参数 15 | if (options.force) { 16 | // 删除重名目录(remove是个异步方法) 17 | await fs.remove(targetDirectory); 18 | } else { 19 | let { isOverwrite } = await new Inquirer.prompt([ 20 | // 返回值为promise 21 | { 22 | name: "isOverwrite", // 与返回值对应 23 | type: "list", // list 类型 24 | message: "Target directory exists, Please choose an action", 25 | choices: [ 26 | { name: "Overwrite", value: true }, 27 | { name: "Cancel", value: false }, 28 | ], 29 | }, 30 | ]); 31 | // 选择 Cancel 32 | if (!isOverwrite) { 33 | console.log("Cancel"); 34 | return; 35 | } else { 36 | // 选择 Overwirte ,先删除掉原有重名目录 37 | console.log("\r\nRemoving"); 38 | await fs.remove(targetDirectory); 39 | } 40 | } 41 | } 42 | // 调用创建项目的 creator 类 43 | const creator = new Creator(projectName, targetDirectory); 44 | 45 | creator.create(); 46 | 47 | }; 48 | 49 | -------------------------------------------------------------------------------- /lib/creator.js: -------------------------------------------------------------------------------- 1 | // Creator.js 2 | 3 | const inquirer = require("inquirer"); 4 | const downloadGitRepo = require("download-git-repo"); 5 | const chalk = require("chalk"); 6 | const util = require("util"); 7 | const path = require("path"); 8 | const { loading } = require("./util"); 9 | const { getMagicalRepo, getTagsByRepo } = require("./api"); 10 | 11 | class Creator { 12 | // 项目名称及项目路径 13 | constructor(name, target) { 14 | this.name = name; 15 | this.target = target; 16 | // download-git-repo 模块并不支持 Promise 17 | // 需要借助 node 的 util 模块提供的 promisify 方法将其转化为支持 Promise 的方法 18 | this.downloadGitRepo = util.promisify(downloadGitRepo); 19 | 20 | } 21 | // 创建项目部分 22 | async create() { 23 | // 仓库信息 —— 模板信息 24 | let repo = await this.getRepoInfo(); 25 | // 标签信息 —— 版本信息 26 | // let tag = await this.getTagInfo(repo); 27 | // 下载模板 28 | // await this.download(repo, tag); 29 | await this.download(repo); 30 | 31 | console.log(this.name, this.target); 32 | console.log(`\r\nSuccessfully created project ${chalk.cyan(this.name)}`); 33 | console.log(`\r\n cd ${chalk.cyan(this.name)}`); 34 | console.log(" npm install"); 35 | console.log(" npm run start\r\n"); 36 | } 37 | 38 | // 获取模板信息及用户最终选择的模板 39 | async getRepoInfo() { 40 | // 获取组织下的仓库信息 41 | let repoList = await loading( 42 | "waiting for fetching template", 43 | getMagicalRepo 44 | ); 45 | if (!repoList) return; 46 | // 提取仓库名 47 | const repos = repoList.map((item) => item.name); 48 | // 选取模板信息 49 | let { repo } = await new inquirer.prompt([ 50 | { 51 | name: "repo", 52 | type: "list", 53 | message: "Please choose a template", 54 | choices: repos, 55 | }, 56 | ]); 57 | return repo; 58 | } 59 | // 获取版本信息及用户选择的版本 60 | async getTagInfo(repo) { 61 | let tagList = await loading( 62 | "waiting for fetching version", 63 | getTagsByRepo, 64 | repo 65 | ); 66 | if (!tagList) return; 67 | const tags = tagList.map((item) => item.name); 68 | // 选取模板信息 69 | let { tag } = await new inquirer.prompt([ 70 | { 71 | name: "tag", 72 | type: "list", 73 | message: "Please choose a version to create project", 74 | choices: tags, 75 | }, 76 | ]); 77 | return tag; 78 | } 79 | 80 | // async download(repo,tag) { 81 | async download(repo) { 82 | // 模板下载地址 83 | // const templateUrl = `Magical-cli-v-1/${repo}${tag ? "#" + tag : ""}`; 84 | const templateUrl = `Magical-cli-v-1/${repo}`; 85 | // 调用 downloadGitRepo 方法将对应模板下载到指定目录 86 | await loading( 87 | "downloading template, please wait", 88 | this.downloadGitRepo, 89 | templateUrl, 90 | path.resolve(process.cwd(), this.target) // 项目创建位置 91 | ); 92 | } 93 | 94 | } 95 | 96 | module.exports = Creator; -------------------------------------------------------------------------------- /bin/enter.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // 导入命令行指令配置 4 | const program = require("commander"); 5 | 6 | // 导入命令行样式美化工具 7 | const chalk = require("chalk") 8 | 9 | // 导入交互式工具 10 | const inquirer = require("inquirer") 11 | 12 | // 导入loading效果 13 | const ora = require('ora') 14 | 15 | // 导入生成ASCII的艺术字工具 16 | const figlet = require('figlet') 17 | 18 | // 生成艺术字 19 | console.log( 20 | "\r\n" + 21 | figlet.textSync("magical", { 22 | horizontalLayout: "default", 23 | verticalLayout: "default", 24 | width: 50, 25 | whitespaceBreak: true, 26 | }) 27 | ); 28 | 29 | // 设置 --help 的提示信息 30 | program 31 | // 版本信息 32 | .version(`magical-cli ${require("../package.json").version}`) 33 | 34 | // cli 名称 35 | .name("Hello!") 36 | 37 | // --help 第一行提示 38 | .usage(`I am a magical cli`); 39 | 40 | // 配置 create 命令 41 | 42 | program 43 | .command("create ") 44 | .description("create a new project") 45 | .option("-f, --force", "overwrite target directory if it exists") // 强制覆盖 46 | .action((projectName, cmd) => { 47 | // 引入 create 模块,并传入参数 48 | require("../lib/create.js")(projectName, cmd); 49 | }); 50 | // 配置 config 命令 51 | // program 52 | // .command("config [value]") // config 命令 53 | // .description("inspect and modify the config") 54 | // .option("-g, --get ", "get value by key") 55 | // .option("-s, --set ", "set option[key] is value") 56 | // .option("-d, --delete ", "delete option by key") 57 | // .action((value, keys) => { 58 | // // value 可以取到 [value] 值,keys会获取到命令参数 59 | // console.log(value, keys); 60 | // }); 61 | 62 | // 监听 --help 指令 63 | program.on("--help", function () { 64 | // 前后两个空行调整格式,更舒适 65 | console.log(); 66 | console.log( 67 | `For example:\n ${chalk.cyan( 68 | "mg " 69 | )} ${chalk.green( 70 | "create app" 71 | )}\n ${chalk.cyan( 72 | "magical" 73 | )} ${chalk.green( 74 | "create app" 75 | )}` 76 | ); 77 | console.log(); 78 | }); 79 | 80 | // 解析用户执行时输入的参数 81 | program.parse(process.argv); 82 | 83 | // 测试 chalk 84 | // console.log(`${chalk.blue("Hello Dear!")}`); 85 | 86 | // console.log(chalk.blue.bgYellowBright.bold("Hello world!")); 87 | 88 | // console.log( 89 | // chalk.green( 90 | // "I am a green line " + 91 | // chalk.red.underline.bold("with a blue substring") + 92 | // " that becomes green again!" 93 | // ) 94 | // ) 95 | 96 | // 交互式功能实现 97 | 98 | // const prompts = [ 99 | // { 100 | // "name": "features", // 选项名称 101 | // "message": "Check the features needed for your project:", // 选项提示语 102 | // "pageSize": 10, 103 | // "type": "list", // 选项类型 另外还有 confirm list 等 104 | // "choices": [ // 具体的选项 105 | // { 106 | // "name": "Babel", 107 | // "value": "babel", 108 | // "short": "Babel", 109 | // "description": "Transpile modern JavaScript to older versions (for compatibility)", 110 | // "link": "https://babeljs.io/", 111 | // "checked": true 112 | // }, 113 | // { 114 | // "name": "Router", 115 | // "value": "router", 116 | // "description": "Structure the app with dynamic pages", 117 | // "link": "https://router.vuejs.org/" 118 | // }, 119 | // ] 120 | // } 121 | // ] 122 | 123 | // inquirer.prompt(prompts).then((data) => { 124 | // console.log(data); 125 | // }); 126 | 127 | 128 | 129 | // // 定义一个loading 130 | // const spinner = ora("Loading unicorns"); 131 | 132 | // // 启动loading 133 | // spinner.start(); 134 | 135 | // setTimeout(() => { 136 | // spinner.color = "yellow"; 137 | // spinner.text = "Loading rainbows"; 138 | // }, 1000); 139 | 140 | // // loading 成功 141 | // spinner.succeed(); 142 | // // loading 失败 143 | // spinner.fail(); 144 | 145 | --------------------------------------------------------------------------------