Promotion and salary increase weapon – take your hand to construct a static page generation scaffold based on pug

Time:2021-8-25

Warm tip: the content is very long, and it records my step-by-step thinking steps. If you can’t watch it and want to try the following or want to see the code, please drag it to the end

My introduction

class BEON {
    name: string;
    sex: string;
    ability: number;

    constructor() {
        this.name = 'BEON';
        this.sex = '♂';
        this.ability = -1;
    }
}

Why make a static page generation scaffold

In this age of Vue, react and angular front-end frameworks, we are used to the development of single page for the preparation of front-end pages, but we do not know that such a group of people are working hard to write static pages and do page template work. They worked hard to add Ctrl + C and Ctrl + V in different static pages, thinking that they were happy. This list (or layout, or…) had been written before. Let me find it quickly, and I don’t have to write a piece of code.
Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug
But since we have all written it, why can’t we use components and instructions like the three frameworks? In a word, there is no pressure. In order to reduce the work pressure for all the big guys, this chicken is working.

Basic content of scaffold

I’m also a vegetable chicken. I didn’t know how to make a scaffold until I read the boss’s articleWater monster boss

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Start thinking

Let’s sort out the contents of our scaffold:

  • entrance
  • Create project create
  • Run dev
  • Package build
  • Add new item add

Emmm… As a chicken dish, it seems very complicated. I want to give up —

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Boss: come on, young man. I’m going to change a new car at the end of the year

Me: good boss

Let’s take it step by step.

entrance

functional requirement

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

In fact, what our main portal needs to do is register the instructions, and then prompt some version numbers. Here is the simplest registration.

Scaffold formwork

program
    .command('create')
    . description ('I can create an item with ')
    .alias('c')
    .action(() => {
        Console. Log ('I have created a project( False);
    })
//Then add the output
program
    .version(require('./package.json').version, '-v --version')
    .parse(process.argv);
    
if (!process.argv.slice(2).length) {
    program.outputHelp();
}

After the execution of beon, we got such a result. The key of mizuzi entrance has been completed!

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug
However, some unfamiliar partners may not know where to run the registration, so we need to configure the basic scaffold. I won’t explain in detail how to configure it. I’ll give you a small template to use:Scaffold project template
For local operation, you only need to pull down the code and run NPM link after installing dependencies!! Then the instruction is beon test

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Several configurations in package.json are described

{
  "Name": "cli model"
  "Version": "1.0.0", // version number
  "Description": "cli scaffold template"
  "Main": "main. JS"
  "scripts": {},
  "bin": {
    "beon-test": "./bin/cmd" // !! Important, the first one is your instruction name
  },
  "keywords": [
    "Cli model" // useless
  ],
  "dependencies": {
    "Commander": "^ 5.0.0" // package
  },
  "author": "wyx",
  "license": "ISC"
}

Code function

Ha, as long as the big guys with strong hands-on ability have successfully completed their first small scaffold entrance, then we will start to write the static page scaffold!, The basic structure is as follows:
Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

This writing is much more convenient for our later expansion

Boss: and the idea of scoring function and data. I’ll give you chicken legs at the end of the year

import program from 'commander';

import create from './create'; //  Project creation
import dev from './dev'; //  Project start
import build from './build'; // Pack Project 
import add from './add'; //  New site

let actionMap = {
    //Project creation
    create: {
        Description: 'create a new item', // description
        Usages: [/ usage method
            'beon create'
        ],
        Alias: 'C' // command abbreviation
    },
    //Start project
    dev: {
        Description: 'local startup project',
        usages: [
            'beon dev'
        ],
        alias: 'd'
    },
    //Pack
    build: {
        Description: 'server project packaging',
        usages: [
            'beon build'
        ],
        alias: 'b'
    },
    //New site
    add: {
        Description: 'create a new site', // description
        Usages: [/ usage method
            'beon add'
        ],
        Alias: 'a' // command abbreviation
    }
}

Object.keys(actionMap).forEach(action => {
    program
        .command(action)
        .description(actionMap[action].description)
        .alias(actionMap[action].alias)
        .action(() => {
            switch (action) {
                case 'create':
                    create();
                    break;
                case 'dev':
                    dev();
                    break;
                case 'build':
                    build();
                    break;
                case 'add':
                    add();
                    break;
                default:
                    break;
            }
        })
});

program
    .version(require('../package.json').version, '-v --version')
    .parse(process.argv);

if (!process.argv.slice(2).length) {
    program.outputHelp();
}

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug
This completes our entrance requirements

Project create

Create target

When we execute the Create method, we should do the following

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Then build our code step by step!

Ask questions to get basic information

For the questioner, you need to use the inquirer package. The use method is also very simple:

inquirer
    .prompt({
        type: 'input',
        name: 'ProjectName',
        Message: 'enter the item name'
    })
    .then(answer => {
        console.log(answer);
    })

This is a very simple questioner. Then we can package promise and use async await to write code. It looks very comfortable

const { ProjectName } = await new Promise(resolve => {
    inquirer
        .prompt({
            type: 'input',
            name: 'ProjectName',
            Message: 'enter the item name'
        })
        .then(answer => {
            resolve(answer);
        })
});

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

The next step is to make the function of our project. The same idea is to use data circulation for registration, which is convenient for later expansion.

//Ask user
let promptList = [{
        type: 'list',
        name: 'frame',
        Message: 'select a template or an existing test item',
        choices: ['single', 'sites']
    },
    {
        type: 'input',
        name: 'description',
        Message: 'enter the item description:'
    },
    {
        type: 'input',
        name: 'author',
        Message: 'please enter your name:'
    }
];

let prompt = () => {
    return new Promise(resolve => {
        inquirer
            .prompt(promptList)
            .then(answer => {
                resolve(answer);
            })
    });
}

Download project template

This requires the support of the tripartite package made by the online bossdownload-git-repo !

The use method is also very simple and rough. Just introduce it directly. Here we will put the project code directly

import downloadGit from 'download-git-repo';

//Remote download of project template
let downloadTemplate = async(ProjectName, api) => {
    return new Promise((resolve, reject) => {
        downloadGit(api, ProjectName, { clone: true }, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        })
    });
};

Update package.json

File??? Here comes readfilesync

//Update JSON configuration file
let updateJsonFile = (fileName, obj) => {

    return new Promise(resolve => {
        if (fs.existsSync(fileName)) {
            const data = fs.readFileSync(fileName).toString();
            let json = JSON.parse(data);
            Object.keys(obj).forEach(key => {
                json[key] = obj[key];
            });
            fs.writeFileSync(fileName, JSON.stringify(json, null, '\t'), 'utf-8');
            resolve();
        }
    });
}

Emmm… I don’t think I can say anything after searching a lot of these things on the Internet. It’s over after a look.

Configure project mode

Now let’s talk about the idea of project mode. The following two modes are realized and designed:

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

There are some differences between the two modes in this area, which will be explained later in the dev and build instructions

Simply put, there is no sites folder for a single project, and the contents of the project are in SRC; Multiple projects have a sites folder, and each file in it is a project, and the SRC folder is used as a place to store public JS, CSS and Pug templates

Combined with the above content, we will write code

if (answer.frame === 'sites') {
    const siteName = 'test';
    fs.mkdirSync(path.resolve(`${ProjectName}/sites`));
    addSites(siteName, `${ProjectName}/`)
    Console.log (symbol.success, chalk. Green ('site creation completed ');
    Await loadcmd ('rm - RF * `, 'delete single site file', ProjectName + '/ SRC / Pug')
}

We created the sites folder and deleted all Src / Pug files to avoid misunderstanding(All contents are operated on the basis of the template, which is a single item mode

Release the template hereGit address(all addresses will be sorted out finally)

Install dependent run

In fact, there is not much to write about this. Everyone is NPM I and then runs it, but we directly use node, so we need to use exec and spawn,
This puts an actuator method.

const util = require("util");
const exec = util.promisify(require("child_process").exec);
const spawn = util.promisify(require("child_process").spawn);

let loadCmd = async(cmd, text, cd, check) => {
    let loading = ora();
    const runner = check ? spawn : exec;
    Loading. Start (` ${text}: command execution... `);
    if (cd) {
        await runner(cmd, { cwd: path.resolve(process.cwd(), cd), detached: true, shell: true });
    } else {
        await runner(cmd);
    }
    Loading. Successful (` ${text}: command execution completed `);
}

We can see that we also wrapped it with promise, which turned into promise, which is more convenient to use.

Personal thinking

In fact, the whole creation is not complicated, but the idea of corresponding processing multiple modes is not so easy to think of. The implementation is very simple, and many scaffolds are not directly generated through JS, but by downloading templates online.

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Dev, run!

We have entered a new stage of shipping and started to use webpack. It’s the same. I won’t introduce too much webpack here. I’ll start directly on the basis of understanding webpack!

Analysis function

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

At first glance, it looks very complex. In fact, it is only written in detail. This process is not the order of code execution, but the order of our ideas.

Now let’s startwebpackConfigure it.

directory structure

I think it’s necessary not to start with the code, but to talk about the structure in advance, which can simplify the complexity of our construction (the general webpack configuration will be relatively long), so as to increase the readability of our code

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

You can see that when I write here, the whole webpack is configured as three parts, and there is a main entry file calleddev.jsIn this way, our functional division is very obvious.

  • Dev.js is responsible for some additional configuration at runtime, such as port detection
  • Webpack.dev.js is a configuration that runs only when dev is running
  • Webpack.config.js is a public configuration
  • Webpack.build.js is the configuration at the time of construction (after that, the structure will not be explained later)

dev.js

This is the smallest of these files. Just take it out and have a look. I mainly know a plug-inportfinderCan help us detect port occupancy.

await portfinder
    .getPortPromise({
        port: baseConfig.devServer.port || port || devPort
    })
    .then(port => {
        devPort = port;
        //
        // `port` is guaranteed to be a free port
        // in this scope.
        //
    })
    .catch(err => {
        console.log(err)
        //
        // Could not get a free port, `err` contains the reason.
        //
    });
new WebpackDevServer(webpack(baseConfig), baseConfig.devServer)
    .listen(devPort, 'localhost', function (err, result) {
        if (err) {
            console.log(err);
        }
});

This is the function of dev. it detects the port and then runs webpack. The function is simple and single

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

There should be no problem. The next step is the configuration of webpack. Because there are too many contents, it’s better to take the code and read the official documents. Here are some main contents

Build Pug file as HTML

The plug-in will be used again hereHtmlWebpackPlugin(the chicken used to only use plug-ins.)

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Then I just need to set certain parameters to successfully load our Pug file

plugins: [
    New webpack. Hotmodulereplacementplugin(), // hot update plug-in (leave a comment without explanation)
    ...tool.getTpl(siteName).map(file => {
        return new HtmlWebpackPlugin({
            template: file.path,
            filename: file.filename + '.html',
            Chunks: ['main', 'vendor' | null], // the public file main.js is introduced here
            Chunkssortmode: 'manual', // sort chunks in the order of introduction
            Inject: true // all JavaScript resources are inserted into the bottom of the body element
        })
    }),
]

Me: we just need to register in this way

Boss:??? Did you fall from the sky?

Me: isn’t it automatic? ☺

<img width=”200″>

For file acquisition, we only need to traverse the pug suffix file under the folder, just like this

const returnPath = siteName === false ? './src/pug/' : `./sites/${siteName}/pug/`
const files = glob
    .sync(siteName === false ? './src/pug/*.pug' : `./sites/${siteName}/pug/*.pug`)
    .map(filepath => {
        const list = filepath.split(/[\/|\/\/|\\|\\\\]/g); //  Slash split file directory
        const fullname = list[list.length - 1].replace(/\.js/g, '');
        //Get the filename of the file
        const name = fullname.substring(0, fullname.lastIndexOf('.'));
        return {
            path: returnPath +
                pathSeparator +
                name +
                '.pug',
            filename: name
        };
    });

Of course, you can just look at this code, because it distinguishes between multi project and single project, and all unknown parameters appear

Routing entry HTML

In the above section of the code that looks at Lehe, the following is the production of the entry HTML template. In fact, it is the HTML code fragment that generates the HTML file

${files
            .map(f => {
                return `        <a href="${f.filename + '.html'}">${
                    f.filename
                    }</a><br>`;
            })
            .join('\n')}
    </body>
</html>`;

The code looks simple and crude

In this way, a basic dev runner is built. Don’t you run a wave quickly?

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

< img width = “100” / > such a simple hello world presents the results that programmers keep looking for (I can’t make it up).

OK, so we have successfully built our dev

Build pack

Now that we can all run successfully, the configuration of build is almost the same. We only need to add a little output to be perfect (of course impossible),The key to configuration is to pull down the project

Today, let’s just look at build. JS.

Console.log (symbol.success, chalk. Green ('Start packaging ');
webpack(baseConfig, async (error) => {
   if(error !== null){
       console.log(symbol.error, chalk.greenBright(error));
   }else{
       setTimeout(() => {
           Loadcmd (` RM main. JS `, 'delete main. JS','. / dist / JS')
       });
       Console.log (symbol.success, chalk. Green ('packaging completed ');
   }
   // process.exit(1);
});

Here, I deleted main.js because of a strange requirement, which makes my packaged project look like it was packaged ☺

Add add item

After the build water has passed, this needs to be written well (no one is looking at the water in the middle)

Sort out the add structure

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Looking at the whole process, the key point is to switch from single project to multi project. Finally, I think it is still necessary to delete the single project file to avoid misunderstanding caused by multi person development (of course, I will be prompted when selecting)

The whole process code is as follows

const site = await newPrompt();
const siteName = site.siteName;
//Site name cannot be empty
if (siteName.length === 0) {
    Console.log (symbol.error, chalk. Greenbright ('Please enter the site name when creating a new site ');
} else {
    //If the file name does not exist, continue, otherwise exit
    await notExistFold(path.resolve('sites') + `/${siteName}`);
    Console.log (chalk. Bluebright ('Start creating new site directory ');
    try {
        const isHave = await notExistFold(path.resolve('sites'), true);
        if (isHave !== true) {
            const answer = await new Promise(resolve => {
                inquirer
                    .prompt([{
                        type: 'list',
                        name: 'warning',
                        Message: 'the current project is in single site mode. Are you sure to convert to multi site (the pug in SRC cannot be packaged after conversion)',
                        choices: ['yes', 'no']
                    }])
                    .then(answer => {
                        resolve(answer.warning);
                    })
            });
            if (answer === 'yes') {
                fs.mkdirSync(path.resolve('sites'));
            } else {
                return ;
            }
        }
        addSites(siteName);
        Console.log (chalk. Bluebright ('New site succeeded! ');
    } catch ( e ) {
        Console.log (symbol.error, chalk. Greenbright ('failed to create directory, please manually detect the problem ');
    }
}

In fact, switching is more like an integrated understanding of the content mentioned above. If interested leaders can manually rewrite add, they can review the previous content.

Conclusion

So far, the explanation of scaffold is basically completed. If you have any questions to comment on, I will help you answer them as much as possible. This is the first time I have written an article in detail. Of course, there will be many deficiencies and there will be bugs in the code(what? There are no bugs in my code!), you are welcome to ask questions. If you are interested, it will be updated from time to time later.

Scaffold code address:https://gitee.com/missshen/be…

Template project address:https://gitee.com/missshen/model

NPM package installation command: cnpm I – G beon page cli

Operation instruction:

  • Create: beon create or beon C
  • Run: beon dev or beon D
  • Packaging: bean build or bean B
  • Add: beon add or beon a

Remember to run NPM run watch when you are running!!! In this way, the code after Babel can be generated in real time, and then NPM link can be developed and run locally.

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug

Finally, the directory structure of the previous package (I can’t see that it is a packaged project)

Promotion and salary increase weapon - take your hand to construct a static page generation scaffold based on pug