├── .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 |
5 |
6 |
7 |
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 |
--------------------------------------------------------------------------------