├── .DS_Store ├── .gitignore ├── README.md ├── bin ├── create-entry-point.js ├── index.js ├── middleware-task.js ├── options │ ├── default-options.js │ ├── index.js │ └── questions │ │ ├── package-name.js │ │ ├── port.js │ │ └── select-middleware.js ├── task │ ├── BaseTask.js │ ├── CommandTask.js │ ├── FileTask.js │ ├── FolderTask.js │ ├── TaskManager.js │ └── index.js ├── template │ └── index.ejs └── utils │ └── exec.js ├── package-lock.json ├── package.json └── test ├── FolderTask.test.js ├── TaskManager.test.js └── create-entry-point.test.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuixiaorui/koa-setup/7f51abc56e7537aa61632746db5ee4dd16f6cca8/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to koa-setup 👋

2 |

3 | 4 | Version 5 | 6 | 7 | License: ISC 8 | 9 |

10 | 11 | > koa setup 12 | 13 | 14 | ## Feature 15 | 16 | 1. 交互式 setup koa 开发坏境 17 | 2. 支持以下中间件 18 | - koa-views 19 | - koa-router 20 | - koa-static 21 | - koa-body 22 | 23 | 24 | ## TODO 25 | 26 | - [ ] 格式化生成的 index.js 27 | - [ ] task 支持优先级的概念 28 | - [ ] 增加 task 组合的概念 29 | - [ ] 并行 30 | - [ ] 串行 31 | - [ ] 抽离整个 task 概念为单独一个 32 | 33 | ### 🏠 [Homepage](https://github.com/cuixiaorui/koa-setup) 34 | 35 | ## Install 36 | 37 | ```sh 38 | npm install koa-setup -g 39 | ``` 40 | 41 | > 注意 nodejs 版本必须大于 12 42 | ## Usage 43 | 44 | ```sh 45 | koa-setup 46 | ``` 47 | 48 | ## Run tests 49 | 50 | ```sh 51 | npm test 52 | ``` 53 | 54 | ## Author 55 | 56 | 👤 **cuixiaorui** 57 | 58 | * Website: beijing 59 | * Github: [@cuixiaorui](https://github.com/cuixiaorui) 60 | 61 | ## Show your support 62 | 63 | Give a ⭐️ if this project helped you! 64 | 65 | *** 66 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ 67 | -------------------------------------------------------------------------------- /bin/create-entry-point.js: -------------------------------------------------------------------------------- 1 | const ejs = require("ejs"); 2 | const fs = require("fs-extra"); 3 | var remark = require("remark"); 4 | var recommended = require("remark-preset-lint-recommended"); 5 | var lint = require('remark-lint'); 6 | 7 | module.exports = function createEntryPointCode(options) { 8 | const templateCode = fs.readFileSync(__dirname + "/template/index.ejs"); 9 | 10 | const processedCode = ejs.render(templateCode.toString(), options); 11 | return new Promise((resolve, reject) => { 12 | resolve(processedCode) 13 | }) 14 | 15 | // todo 格式化生成代码 16 | // return new Promise((resolve, reject) => { 17 | // remark() 18 | // .use(lint) 19 | // .process(processedCode, function (err, file) { 20 | // if (err) { 21 | // reject(err); 22 | // return; 23 | // } 24 | // resolve(String(file)); 25 | // }); 26 | // }); 27 | }; 28 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // 1. 在当前目录下创建文件夹 4 | // 2. 安装插件 - koa koa-router koa-static koa-views 5 | // 1. 可交互选择安装 6 | // 3. index.js 这个文件的模板 7 | // 1. 需要基于安装的插件动态生成 8 | const chalk = require("chalk"); 9 | const log = console.log; 10 | const getOptions = require("./options"); 11 | const path = require("path"); 12 | const MiddlewareTask = require("./middleware-task"); 13 | const createEntryPointCode = require("./create-entry-point"); 14 | const { FileTask, FolderTask, CommandTask, TaskManager } = require("./task"); 15 | let options; 16 | 17 | (async () => { 18 | options = await getOptions(); 19 | 20 | console.log(options); 21 | const taskManager = new TaskManager(); 22 | // 创建项目文件夹 23 | taskManager.add(createPackageTask()); 24 | 25 | // 创建入口文件 index.js 26 | const code = await createEntryPointCode(options); 27 | taskManager.add(createEntryPointFileTask(code)); 28 | 29 | // 创建 npm package.json 30 | taskManager.add(initNpmTask()); 31 | 32 | // 添加中间件的任务 33 | taskManager.add(MiddlewareTask(options.middleware, getRoot())); 34 | 35 | taskManager.add(installKoa()); 36 | 37 | await taskManager.execute(); 38 | 39 | // 安装结束 40 | log(chalk.hex("#7FFF00").bold(`cd ./${options.packageName} && nodemon index.js`)); 41 | log(chalk.hex("#DEADED").bold("happy every day -_-#")); 42 | })(); 43 | 44 | function getRoot() { 45 | return path.resolve(process.cwd(), options.packageName); 46 | } 47 | 48 | function createPackageTask() { 49 | return new FolderTask({ 50 | name: "create package folder", 51 | path: getRoot(), 52 | }); 53 | } 54 | 55 | function installKoa() { 56 | return new CommandTask({ 57 | command: `cd ${getRoot()} && npm i koa`, 58 | name: "npm i koa", 59 | }); 60 | } 61 | 62 | function initNpmTask() { 63 | return new CommandTask({ 64 | command: `cd ${getRoot()} && npm init -y`, 65 | name: "npm init -y", 66 | }); 67 | } 68 | 69 | function createEntryPointFileTask(content) { 70 | return new FileTask({ 71 | content, 72 | name: "create entry point index.js", 73 | filename: "index.js", 74 | path: getRoot(), 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /bin/middleware-task.js: -------------------------------------------------------------------------------- 1 | const FolderTask = require("./task/FolderTask"); 2 | const CommandTask = require("./task/CommandTask"); 3 | 4 | module.exports = (middlewareList, root) => { 5 | const map = getMiddlewareTaskMap(root); 6 | return middlewareList.reduce((result, name) => { 7 | const tasks = map[name]; 8 | result.push(...tasks); 9 | return result; 10 | }, []); 11 | }; 12 | 13 | function getMiddlewareTaskMap(root) { 14 | const createInstallCommandTask = (command) => { 15 | const cdToRootPathCommand = `cd ${root}`; 16 | return new CommandTask({ 17 | name: command, 18 | command: `${cdToRootPathCommand} && ${command}`, 19 | }); 20 | }; 21 | 22 | return { 23 | koaStatic: [ 24 | new FolderTask({ 25 | path: root, 26 | filename: "static", 27 | name: "create static folder", 28 | }), 29 | createInstallCommandTask("npm i koa-static"), 30 | ], 31 | koaViews: [ 32 | new FolderTask({ 33 | path: root, 34 | filename: "views", 35 | name: "create views folder", 36 | }), 37 | createInstallCommandTask("npm i koa-views pug"), 38 | ], 39 | koaRouter: [createInstallCommandTask("npm i koa-router")], 40 | 41 | koaBody: [createInstallCommandTask("npm i koa-body")], 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /bin/options/default-options.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: "8080", 3 | } -------------------------------------------------------------------------------- /bin/options/index.js: -------------------------------------------------------------------------------- 1 | var inquirer = require("inquirer"); 2 | const defaultOptions = require("./default-options"); 3 | const packageName = require("./questions/package-name"); 4 | const port = require("./questions/port"); 5 | const selectMiddleware = require("./questions/select-middleware"); 6 | 7 | module.exports = async () => { 8 | const answers = await inquirer.prompt([ 9 | packageName(), 10 | port(defaultOptions), 11 | selectMiddleware(), 12 | ]); 13 | 14 | return handleOptions(answers); 15 | }; 16 | 17 | function handleOptions(options) { 18 | let result = Object.assign({}, options); 19 | result.middlewareMap = options.middleware.reduce((result, val) => { 20 | result[val] = true; 21 | return result; 22 | }, {}); 23 | return result; 24 | } 25 | -------------------------------------------------------------------------------- /bin/options/questions/package-name.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | type: "input", 3 | name: "packageName", 4 | message: "set package name", 5 | validate(value) { 6 | if (value) { 7 | return true; 8 | } 9 | return "Please enter package name"; 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /bin/options/questions/port.js: -------------------------------------------------------------------------------- 1 | module.exports = (options) => { 2 | return { 3 | type: "input", 4 | name: "port", 5 | message: "set server port number", 6 | default() { 7 | return options.port; 8 | }, 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /bin/options/questions/select-middleware.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | type: "checkbox", 3 | message: "Select middleware", 4 | name: "middleware", 5 | choices: [ 6 | { 7 | name: "koaRouter", 8 | }, 9 | { 10 | name: "koaStatic", 11 | }, 12 | { 13 | name: "koaViews", 14 | }, 15 | 16 | { 17 | name: "koaBody", 18 | }, 19 | ], 20 | }); 21 | -------------------------------------------------------------------------------- /bin/task/BaseTask.js: -------------------------------------------------------------------------------- 1 | module.exports = class BaseTask { 2 | constructor(type, name = "") { 3 | this.name = name; 4 | this.type = type; 5 | } 6 | 7 | getType() { 8 | return this.type; 9 | } 10 | 11 | getName() { 12 | return this.name; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /bin/task/CommandTask.js: -------------------------------------------------------------------------------- 1 | const BaseTask = require("./BaseTask"); 2 | const exec = require("../utils/exec"); 3 | 4 | module.exports = class CommandTask extends BaseTask { 5 | static type = "command"; 6 | constructor({ command, name }) { 7 | super(CommandTask.type, name); 8 | this.command = command; 9 | } 10 | 11 | execute() { 12 | return exec(this.command); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /bin/task/FileTask.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs-extra"); 2 | const path = require("path"); 3 | const BaseTask = require("./BaseTask"); 4 | 5 | module.exports = class FileTask extends BaseTask { 6 | static type = "file"; 7 | constructor({ path, filename, content, name }) { 8 | super(FileTask.type, name); 9 | this.path = path; 10 | this.filename = filename; 11 | this.content = content; 12 | } 13 | 14 | getFullPath() { 15 | return path.resolve(this.path, this.filename); 16 | } 17 | 18 | execute() { 19 | return fs.outputFile(this.getFullPath(), this.content); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /bin/task/FolderTask.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs-extra"); 2 | const path = require("path"); 3 | const BaseTask = require("./BaseTask"); 4 | module.exports = class FolderTask extends BaseTask { 5 | static type = "folder"; 6 | constructor({ path, filename = "",name }) { 7 | super(FolderTask.type,name); 8 | this.path = path; 9 | this.filename = filename; 10 | } 11 | 12 | getFullPath() { 13 | return path.resolve(this.path, this.filename); 14 | } 15 | 16 | execute() { 17 | return fs.ensureDir(this.getFullPath()); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /bin/task/TaskManager.js: -------------------------------------------------------------------------------- 1 | const { FileTask, FolderTask, CommandTask } = require("./index"); 2 | const chalk = require("chalk"); 3 | const log = console.log; 4 | module.exports = class TaskManager { 5 | constructor() { 6 | this.taskList = []; 7 | } 8 | 9 | add(task) { 10 | if (Array.isArray(task)) { 11 | this.taskList.push(...task); 12 | } else { 13 | this.taskList.push(task); 14 | } 15 | } 16 | 17 | size() { 18 | return this.taskList.length; 19 | } 20 | 21 | getTasksByType(type) { 22 | return this.taskList.filter((task) => { 23 | return task.getType() === type; 24 | }); 25 | } 26 | 27 | async execute() { 28 | // 执行命令需要有前后顺序 29 | // 先执行所有创建文件夹的task 30 | await this.executeTaskByType(FolderTask.type); 31 | 32 | // 在执行所有文件的task 33 | await this.executeTaskByType(FileTask.type); 34 | 35 | //执行所有的 command task 36 | await this.executeTaskByType(CommandTask.type); 37 | 38 | } 39 | 40 | async executeTaskByType(commandType) { 41 | const commandTasks = this.getTasksByType(commandType); 42 | for (const task of commandTasks) { 43 | log(chalk.blue("当前执行的任务:") + chalk.red(task.name)); 44 | await task.execute(); 45 | } 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /bin/task/index.js: -------------------------------------------------------------------------------- 1 | exports.FileTask = require('./FileTask'); 2 | exports.FolderTask = require('./FolderTask'); 3 | exports.CommandTask = require('./CommandTask'); 4 | exports.TaskManager = require('./TaskManager'); -------------------------------------------------------------------------------- /bin/template/index.ejs: -------------------------------------------------------------------------------- 1 | const Koa = require("koa") 2 | <% if (middlewareMap.koaRouter) { %> 3 | const Router = require("koa-router") 4 | <% } %> 5 | <% if (middlewareMap.koaStatic) { %> 6 | const serve = require("koa-static") 7 | <% } %> 8 | <% if (middlewareMap.koaViews) { %> 9 | const views = require("koa-views") 10 | <% } %> 11 | 12 | <% if (middlewareMap.koaBody) { %> 13 | const koaBody = require("koa-body") 14 | <% } %> 15 | 16 | const app = new Koa() 17 | 18 | <% if (middlewareMap.koaStatic) { %> 19 | app.use(serve(__dirname+"/static")) 20 | <% } %> 21 | 22 | <% if (middlewareMap.koaViews) { %> 23 | app.use(views(__dirname+"/views",{ 24 | extension: "pug" 25 | })) 26 | <% } %> 27 | 28 | <% if (middlewareMap.koaBody) { %> 29 | app.use(koaBody({ 30 | multipart:true 31 | })) 32 | <% } %> 33 | 34 | <% if (middlewareMap.koaRouter) { %> 35 | const router = new Router() 36 | router.get("/",(ctx)=>{ 37 | ctx.body = "hello <%= packageName %>" 38 | }) 39 | app.use(router.routes()) 40 | <% } %> 41 | 42 | app.listen(<%= port %>,()=>{ 43 | console.log("open server localhost:<%= port %>") 44 | }) 45 | -------------------------------------------------------------------------------- /bin/utils/exec.js: -------------------------------------------------------------------------------- 1 | const { exec } = require("child_process"); 2 | module.exports = (command) => { 3 | return new Promise((resolve, reject) => { 4 | exec(command, (err, stdout, stderr) => { 5 | if (err) { 6 | reject(err); 7 | return; 8 | } 9 | resolve({ stdout, stderr }); 10 | }); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-setup", 3 | "version": "1.1.0", 4 | "description": "koa setup", 5 | "main": "./bin/index.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "bin": "./bin/index.js", 10 | "keywords": [ 11 | "koa" 12 | ], 13 | "engines": { 14 | "node": ">12.0.0" 15 | }, 16 | "author": "cuixiaorui", 17 | "homepage": "https://github.com/cuixiaorui/koa-setup", 18 | "license": "ISC", 19 | "dependencies": { 20 | "commander": "^5.1.0", 21 | "ejs": "^3.1.3", 22 | "inquirer": "^7.1.0", 23 | "koa-body": "^4.1.3", 24 | "koa-router": "^8.0.8", 25 | "koa-static": "^5.0.0", 26 | "koa-views": "^6.2.2", 27 | "remark": "^12.0.0", 28 | "remark-lint": "^7.0.0", 29 | "remark-preset-lint-recommended": "^4.0.0", 30 | "fs-extra": "^9.0.0" 31 | }, 32 | "devDependencies": { 33 | "@types/jest": "^25.2.3", 34 | "jest": "^26.0.1", 35 | "release": "^6.3.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/FolderTask.test.js: -------------------------------------------------------------------------------- 1 | const FolderTask = require("../bin/task/FolderTask"); 2 | describe("FolderTask", () => { 3 | it("get full path ", () => { 4 | const folderTask = new FolderTask({ 5 | path: __dirname, 6 | filename: "test", 7 | }); 8 | 9 | const fullPath = folderTask.getFullPath(); 10 | 11 | expect(fullPath).toBe(__dirname + "/test"); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/TaskManager.test.js: -------------------------------------------------------------------------------- 1 | const FolderTask = require("../bin/task/FolderTask"); 2 | const TaskManager = require("../bin/task/TaskManager"); 3 | describe("TaskManager.test.js", () => { 4 | it("add task ", () => { 5 | // given 6 | const taskManager = new TaskManager(); 7 | const folderTask = new FolderTask({ 8 | path: "", 9 | filename: "", 10 | }); 11 | 12 | // when 13 | taskManager.add(folderTask); 14 | 15 | // then 16 | expect(taskManager.size()).toBe(1); 17 | }); 18 | 19 | it("get tasks by type", () => { 20 | // given 21 | const taskManager = new TaskManager(); 22 | const folderTask1 = new FolderTask({ 23 | path: "", 24 | filename: "", 25 | }); 26 | const folderTask2 = new FolderTask({ 27 | path: "", 28 | filename: "", 29 | }); 30 | 31 | taskManager.add(folderTask1); 32 | taskManager.add(folderTask2); 33 | 34 | // when 35 | const folderTasks = taskManager.getTasksByType("folder"); 36 | 37 | // then 38 | expect(folderTasks.length).toBe(2); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/create-entry-point.test.js: -------------------------------------------------------------------------------- 1 | const createEntryPoint = require("../bin/create-entry-point"); 2 | describe("create-entry-point.test.js", () => { 3 | it("description", async () => { 4 | // todo options 5 | const options = { 6 | packageName: "demo6234234", 7 | port: "8080", 8 | middleware: ["koaRouter", "koaStatic", "koaViews", "koaBody"], 9 | middlewareMap: { 10 | koaRouter: true, 11 | koaStatic: true, 12 | koaViews: true, 13 | koaBody: true, 14 | }, 15 | }; 16 | const code = await createEntryPoint(options); 17 | // const expectation = `'const Koa = require("koa")\n' + 18 | // '\n' + 19 | // 'const Router = require("koa-router")\n' + 20 | // '\n' + 21 | // 'const serve = require("koa-static")\n' + 22 | // '\n' + 23 | // 'const views = require("koa-views")\n' + 24 | // '\n' + 25 | // 'const views = require("koa-body")\n' + 26 | // '\n' + 27 | // 'const app = new Koa()\n' + 28 | // '\n' + 29 | // 'app.use(serve(\\_\\_dirname+"/static"))\n' + 30 | // '\n' + 31 | // 'app.use(views(\\_\\_dirname+"/views",{\n' + 32 | // ' extension: "pug"\n' + 33 | // '}))\n' + 34 | // '\n' + 35 | // 'app.use(koaBody({\n' + 36 | // ' multipart:true\n' + 37 | // '}))\n' + 38 | // '\n' + 39 | // 'const router = new Router()\n' + 40 | // 'router.get("/",(ctx)=>{\n' + 41 | // ' ctx.body = "hello demo6234234"\n' + 42 | // '})\n' + 43 | // 'app.use(router.routes())\n' + 44 | // '\n' + 45 | // 'app.listen(8080)\n' 46 | // }` 47 | console.log(code) 48 | // expect(expectation).toBe(code) 49 | }); 50 | }); 51 | --------------------------------------------------------------------------------