A: hi! ~ Hello everyone, I am YK bacteria ๐Ÿท, a microsystem front-end โœจ, love to think, love to summarize, love to record, love to share ๐Ÿน, welcome to follow me ๐Ÿ˜˜ ~ [wechat account: Yk2012Yk2012, wechat public account: ykyk2012]

“This is the 29th day of my participation in the Gwen Challenge in November. See details of the event: The Last Gwen Challenge in 2021”

Youth_camp_ykjun: Youth_camp_ykjun: Gitee.com

One, foreword

Today we will review and summarize the content of the second day of the youth training camp. In the last lesson, we created a VUE scaffold from 0 to 1. Today, we followed Teacher Cui @Atcui CXR to build a KOA scaffold with automatic thinking

PS: This blog is the first part of teacher Cui’s front-end engineering course: initialization. I will try my best to update it later, including: Webpack principle, rollup principle, mini-Vite implementation, automatic testing and so on

Analyze vuE-CLI

  1. Create a project using the directive
vue create hello-world
Copy the code
  1. Provide single/multiple options

  1. Install dependencies

The summary steps are: โ‘  input instructions โ‘ก select configuration โ‘ข Create files โ‘ฃ install dependencies

So we will follow this step to write our scaffolding

Third, start

Now we are going to implement a scaffold ourselves. With the basics of last class, today’s content can take you to the next level.

Start by creating our scaffold’s project directory learn-koa-setup

Then initialize the project NPM init-y

Change the project modularity specification to ESM by adding “type”: “module” to package.json,

Create index.js and write scaffolding here to do what you want

import fs from "fs";

// Core: Automatic thinking [first think about the basic steps of manually creating a project]

1. Create folder (project name)
fs.mkdirSync("ykyk");

// 2. Create index.js
fs.writeFileSync("./ykyk/index.js"."index");

// 3. Create package.json
fs.writeFileSync("./ykyk/package.json"."package");

4. Install dependencies
// TODO package -> npm
Copy the code

Let’s test that out

node index.js
Copy the code

Folders and files are created and filled with basic content

Every time we execute this command, we need to manually delete the folder ykyk and then create it again, which is quite troublesome. We need to use the instruction to make it execute automatically and add the instruction in package.json (I used the instruction of Windows to delete the folder here).

"scripts": {
  "test": "rd /s ykyk && node index.js"
},
Copy the code

Now you can use the NPM test command to delete the folder and execute the index.js file.

Fourth, perfect

Although the above steps are very easy, but is already the general direction has been clear, the next thing to do is to constantly improve the specific details here.

Note: We are writing the code in small steps. Improve it bit by bit, one small feature at a time, and then test it. We’ll write it down when it works, so that when we’re writing code, we know what’s wrong and we can fix it.

We’re using a lot of root paths here, and we’re encapsulating it

function getRootPath() {
  return "./ykyk";
}
Copy the code

You can use it like this

1. Create folder (project name)
fs.mkdirSync(getRootPath());

// 2. Create index.js
fs.writeFileSync(getRootPath()+"/index.js"."index");

// 3. Create package.json
fs.writeFileSync(getRootPath()+"/package.json"."package");
Copy the code

1. Generate index.js content

โ‘  Generating a Template

We create a template indextemplate.js

Write the template content to death, then export the function that creates the template

export function createIndexTemplate() {
  return ` const Koa = require("koa"); const app = new Koa(); app.listen(8000, () => { console.log("open server localhost:8000"); }); `;
}
Copy the code

Import in index.js and use it

import { createIndexTemplate } from "./indexTemplate.js";

fs.writeFileSync(getRootPath() + "/index.js", createIndexTemplate());
Copy the code

Finally, execute the instruction to see the effect

โ‘ก Dynamically generate templates

The next step is to make the contents of the template render different code depending on the Settings

Here we use a third-party library ejS efficient embedded JavaScript template engine

Install NPM I EJS

Create the file template/index.ejs

const Koa = require("koa");

const app = new Koa();

app.listen(8000.() = > {
  console.log("open server localhost:8000");
});
Copy the code

This is how indexTemplate.js should be introduced and used

import ejs from "ejs";
import fs from "fs";

export function createIndexTemplate() {
  // Read the EJS template
  const template = fs.readFileSync("./template/index.ejs"."utf-8");
  // Give it to ejS to render
  const code = ejs.render(template);
  // Return the rendered template
  return code;
}
Copy the code

At this point, test again and find that there is no problem, and then conditional rendering

Modify the index.ejs file [according to the syntax given in the document]

const Koa = require("koa"); The < %if (router) { %>
const Router = require("koa-router"); The < %} % >const app = newKoa(); The < %if (router) { %>
const router = new Router();
router.get("/".(ctx) = > {
ctx.body = "Hello, YK bacteria." ";
});
app.use(router.routes());
<% } %>

app.listen(8000.() = > {
console.log("open server localhost:8000");
});
Copy the code

It decides whether to render the content of the response by passing arguments to the render function

const code = ejs.render(template, {
  router: true});Copy the code

โ‘ข Simulate the data input by the user

This parameter should be chosen by the user, but we write the code in small steps, so we write out the user’s input first.

// Write the configuration middleware data to death
const inputConfig = {
  middleware: {
    router: true,}};// Pass the configuration file to the create template function
fs.writeFileSync(getRootPath() + "/index.js", createIndexTemplate(inputConfig));
Copy the code

Use this in indextemplate.js

export function createIndexTemplate(inputConfig) {
  // Read the EJS template
  const template = fs.readFileSync("./template/index.ejs"."utf-8");
  // Give it to ejS to render
  const code = ejs.render(template, {
    router: inputConfig.middleware.router,
  });
  // Return the rendered template
  return code;
}
Copy the code

โ‘ฃ Expand the complete options

Let’s complete the four choices

// Program input
const inputConfig = {
  middleware: {
    router: true.static: true.views: true.body: true}};Copy the code

indexTemplate.js

import ejs from "ejs";
import fs from "fs";

export function createIndexTemplate(inputConfig) {
  // Read the EJS template
  const template = fs.readFileSync("./template/index.ejs"."utf-8");
  // Give it to ejS to render
  const code = ejs.render(template, {
    router: inputConfig.middleware.router,
    static: inputConfig.middleware.static,
    views: inputConfig.middleware.views,
    body: inputConfig.middleware.body
  });
  // Return the rendered template
  return code;
}
Copy the code

template/index.ejs

const Koa = require("koa"); The < %if (router) { %>
const Router = require("koa-router"); < %} % > < %if (static) { %>
const serve = require("koa-static"); < %} % > < %if (views) { %>
const views = require("koa-views"); < %} % > < %if (body) { %>
const body = require("koa-body"); The < %} % >const app = newKoa(); The < %if (router) { %>
const router = new Router();
router.get("/".(ctx) = > {
ctx.body = "Hello, YK bacteria." "; }); app.use(router.routes()); < %} % > < %if (static) { %>
app.use(serve(__dirname + "/static")); < %} % > < %if (views) { %>
app.use(
views(__dirname + "/views", {
extension: "pug",
})
);
<% } %>
<% if (body) { %>
app.use(
body({
multipart: true,})); <% } %> app.listen(8000.() = > {
console.log("open server localhost:8000");
});
Copy the code

Test our code functionality NPM test

The index.js in the ykyk project created at this point is the complete code generated from the options

Shows that the content in ykyk/index.js is what we expect

const Koa = require("koa");

const Router = require("koa-router");

const serve = require("koa-static");

const views = require("koa-views");

const body = require("koa-body");

const app = new Koa();

const router = new Router();
router.get("/".(ctx) = > {
  ctx.body = "Hello, YK bacteria." ";
});
app.use(router.routes());

app.use(serve(__dirname + "/static"));

app.use(
  views(__dirname + "/views", {
    extension: "pug",})); app.use( body({multipart: true,})); app.listen(8000.() = > {
  console.log("open server localhost:8000");
});
Copy the code

2. Generate package.json content

We simply generated the index.js template using EJS, and then generated package.json from the data, basically the same steps as above

โ‘  Generating a Template

First create packageTemplate.js

import ejs from "ejs";
import fs from "fs";

export function createPackageTemplate(inputConfig) {
  // Read the EJS template
  const template = fs.readFileSync("./template/package.ejs"."utf-8");
  // Give it to ejS to render
  const code = ejs.render(template, {
    router: inputConfig.middleware.router,
    static: inputConfig.middleware.static,
    views: inputConfig.middleware.views,
    body: inputConfig.middleware.body,
  });
  // Return the rendered template
  return code;
}
Copy the code

Then create a template, template/package.ejs

{
  "name": "koa-setup-ykyk"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"."build": "rm -rf hei && node index.js"
  },
  "keywords": []."author": ""."license": "ISC"."dependencies": {
    "koa": "Tokens ^ 2.13.1"The < %if (router) { %>
    ,"koa-router": "^ 10.1.1"< %} % > < %if (static) {% >,"koa-static": "^ 5.0.0"< %} % > < %if (views) { %>
    ,"koa-views": "^" 7.0.1
    ,"pug": "^ 3.0.2." "< %} % > < %if (body) { %>
    ,"koa-body": "^ 4.1.3." "<%} %>}}Copy the code

A trick here is to put a comma before the optional package to ensure that the format is correct

โ‘ก Obtain the project name

The user is simulated to enter packageName

// Program input
const inputConfig = {
  packageName: 'yk_demo'.middleware: {
    router: true. }};Copy the code

I’m just going to introduce it here

.// Give it to ejS to render
  const code = ejs.render(template, {
    packageName: inputConfig.packageName, ... }); .Copy the code

Ejd (template/package.ejd

"name": "<%= packageName %>".Copy the code

Finally, let’s test the NPM test and see that the generated package.json meets our expectations

{
  "name": "yk_demo"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"."build": "rm -rf hei && node index.js"
  },
  "keywords": []."author": ""."license": "ISC"."dependencies": {
    "koa": "Tokens ^ 2.13.1"."koa-router": "^ 10.1.1"."koa-static": "^ 5.0.0"."koa-views": "^" 7.0.1."pug": "^ 3.0.2." "."koa-body": "^ 4.1.3." "}}Copy the code

In the same way, we can set the port number in index.js to a mode that can be selected by the user. For details, see the code in my repository

3. Obtain user input

The next thing we need to do is get the actual user input. Here we use the Inquirer library (A collection of common Interactive Command Line user interfaces. inquirer – npm (npmjs.com)

First install the library NPM I Inquirer

Create question/index.js to get user input

import inquirer from "inquirer";

export function question() {
  return inquirer.prompt([
    // Get the project name entered by the user
    { type: "input".name: "packageName".message: "set package name" },
    // Get the port number entered by the user. The default value is 8080
    {
      type: "number".name: "port".message: "set port number".default: () = > 8080,},// Multi-select to get the middleware selected by the user
    {
      type: "checkbox".name: "middleware".choices: [{name: "koaRouter" },
        { name: "koaStatic" },
        { name: "koaViews" },
        { name: "koaBody"},],},]); }Copy the code

The simulated data is then replaced with data entered by the real user

import { question } from "./question/index.js";

const answer = await question();

// Program input
const inputConfig = {
  packageName: answer.packageName,
  port: answer.port,
  middleware: {
    router: answer.middleware.indexOf("koaRouter")! = = -1.static: answer.middleware.indexOf("koaStatic")! = = -1.views: answer.middleware.indexOf("koaViews")! = = -1.body: answer.middleware.indexOf("koaBody")! = = -1,}};Copy the code

Here we are using an await, but not an async function wrap, because new (14.8+) versions of Node.js support direct await at the top level

Of course we can take the logic out of index.js to get user input and simplify the code

Create a config.js

export function createConfig(answer) {
  return {
    packageName: answer.packageName,
    port: answer.port,
    middleware: {
      router: haveMiddleware("koaRouter"),
      static: haveMiddleware("koaStatic"),
      views: haveMiddleware("koaViews"),
      body: haveMiddleware("koaBody"),}};function haveMiddleware(name) {
    returnanswer.middleware.indexOf(name) ! = = -1; }}Copy the code

Index.js is introduced directly

import { question } from "./question/index.js";
import { createConfig } from "./config.js";

const answer = await question();
const inputConfig = createConfig(answer);
Copy the code

There we go. Look at the results

File contents ykyk/index.js

const Koa = require("koa");
const Router = require("koa-router");
const views = require("koa-views");
const app = new Koa();

const router = new Router();
router.get("/".(ctx) = > {
  ctx.body = "Hello, YK bacteria." ";
});
app.use(router.routes());

app.use(
  views(__dirname + "/views", {
    extension: "pug",})); app.listen(8080.() = > {
  console.log("open server localhost:8080");
});
Copy the code

File contents ykyk/package.json

{
  "name": "ykyk"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"."build": "rm -rf hei && node index.js"
  },
  "keywords": []."author": ""."license": "ISC"."dependencies": {
    "koa": "Tokens ^ 2.13.1"."koa-router": "^ 10.1.1"."koa-views": "^" 7.0.1."pug": "^ 3.0.2." "}}Copy the code

4. Install dependencies

The last time we used nodeJS native call child process install dependency [youth Camp Pro] ๐Ÿ› ๏ธ from 0 to 1 to implement a own front-end convention routing project scaffolding ๐Ÿ—๏ธ tool ~ – – juejin (cn), Today we use execA-nPM (nPMjs.com), a popular service in the community, to automatically install dependencies

The first step is to install NPM I EXECa

Direct introduction

import { execa } from "execa";

4. Install dependencies
// TODO package -> npm
// Configure the installation path and display the installation status
execa("npm install", {cwd: getRootPath(), stido: [2.2.2]});Copy the code

Test the

Successful startup ~

5. Format code

Format our code API ยท Prettier in the form of an API call for Prettier

Install NPM I Prettier

It is used in indexTemplate.js and packageTemplate.json respectively

.import prettier from "prettier";

export function createIndexTemplate(inputConfig) {...// Return the rendered template
  return prettier.format(code, {
    parser: "babel"}); }Copy the code
.import prettier from "prettier";

export function createPackageTemplate(inputConfig) {...// Return the rendered template
  return prettier.format(code, {
    parser: "json"}); }Copy the code

You can test the effect yourself

6. Troubleshoot the path problem

And finally, let’s deal with our path problem and change the relative path we wrote before to an absolute path

The first is the project root path

.import path from "path"; .function getRootPath() {
  // Add the root path + the project name to get the project root path
  return path.resolve(process.cwd(), inputConfig.packageName);
}
Copy the code

Then there is the question of where indexTemplate.js imports index.ejs

In CommonJS you can use __dirName to get the path, but we’re using ESM here, so we’ll use fileURLToPath in the URL

.import { fileURLToPath } from "url";
import path from "path"; .const __dirname = path.dirname(fileURLToPath(import.meta.url));
  // Read the EJS template
  const template = fs.readFileSync(
    path.resolve(__dirname, "./template/index.ejs"),
    "utf-8"); .Copy the code

That’s the same thing in packageTempalte.js

Five, publish,

The following steps are the same as before ~ [youth Camp Pro] ๐Ÿ› ๏ธ implement a own front-end contract routing project from 0 to 1 scaffolding ๐Ÿ—๏ธ tool ~ – excavation (juejin. Cn) first create a global command to create the project, and then upload to the NPM repository

Place everything you’ve written in the bin directory and modify package.json as you did in the previous steps

Link the project to the global via the NPM link, and then view the global NPM information through the NPM root -g

As you can see the project is now globally linked, we can start our scaffolding project with the learn-koa-setup-yk command

npm config set registry=https://registry.npmjs.org
npm publish
npm config set registry=https://registry.npm.taobao.org
Copy the code

You can share your scaffolding with others

Finally, welcome to my column and make friends with YK bacteria