preface

With the basics behind us (how to build a simple scaffold at the front end), we can now talk about how a full-fledged scaffold works.

Here we refer to vuE-CLI source code, based on rollup and typescript step by step build. Vue-cli has matured as a scaffold for vUE for so many front-end developers.

start

Here our command is still ds~ and the template is ds-cli-lib-template

The directory structure

├ ─ binPackage the file directory│ ├ ─ ds. JsThe bin field in # package.json references the file
├─ src
│  ├─ lib         # Specify the command directory│ ├ ─ the list# ds list│ ├ ─ init# ds init│ ├ ─ utils# utility function├ ─ main. Ts# import file├ ─ typings# typescript type file directory├ ─ rullup. Config. Js# rollpu build configuration├ ─ ─test            # Test cases
Copy the code

Writing build configurations

Today, WebPack is used to develop applications (hot update HMR, code split, etc.) and Rollup is used to develop libraries (easy to use, packaged code can read, and other features webPack already supports).

Now let’s clarify our requirements

  • Write module code in typescript
  • Code packaged into the UMD module specification
  • Packages that can reference commonJS specifications (most packages are not ES module specifications for historical reasons)
  • Compress packaging code to reduce volume
//rollup.config.js
import typescript from "rollup-plugin-typescript2";
import commonjs from 'rollup-plugin-commonjs'
import { uglify } from 'rollup-plugin-uglify'
export default {
    // Import file
  input: "src/main.ts".output: [{banner: "#! /usr/bin/env node"./** * insert this code in the header ** /
      name: "ds".file: "bin/ds.js".// Package as umD module specification
      format: "umd"}].plugins: [
    typescript(),
    commonjs({
      include: "node_modules/**".extensions: ['.js'.'.ts']
    }),
    uglify()
  ],
};
Copy the code

NPM script command (“scripts” field)

{
"clean": "rm -rf ./bin && mkdir bin"."build": "npm run clean && rollup --config"
}
Copy the code

Writing entry files

It’s something very basic, we don’t usually put very complicated logic in the entry file.

const cmd = require('commander');
const config = require('.. /package.json');

Cli-init. ts and cli-list.ts we can simply export a function such as cli-init.ts
// export default function(... args) {
// console.log('init')
// }
import init from './lib/init/cli-init'; 
import list from './lib/list/cli-list';

const command= {
    init,
    list
};

//map corresponds to the type to execute
function exec(type. args) {
    config.debug = args[0].debug;
    command[type] (... args); } cmd .usage('<command>')
  .version(config.version)
  .description('Welcome to DS-CLI');

cmd
  .command('init')
  .description('Initialize component template')
  .action((. args) = > exec('init'. args)); cmd .command('list')
  .description('View Online Component Templates')
  .action((. args) = > exec('list'. args)); cmd.command('help')
  .description('View Help')
  .action((a)= > cmd.help());

// Parse the input parameters
cmd.parse(process.argv);
if(! cmd.args.length) { cmd.help(); }Copy the code

After packing (NPM run build) into the bin folder, configure the bin field of package.json to bin/ds.js, and publish NPM (see quick publish a vue-fullpage component to NPM to try the command).

If you fail, review the above procedure.

Initializing a Template

Our requirements for templates

  • The file directory must contain the template folder in which the required template files are stored

  • Use meta-.js to improve customization (so-called dynamic templates)
  • Template file name specification is DS-CLI – ‘name’ -template, convenient scaffold pull
  • You can not use the online template, if there is a template, you can use it directly. For online templates, you should download the.ds-templates directory (~/.ds-templates) in your local user directory.

Expect to command

ds init <template-name> <app-name> # template name and application name
Copy the code

Flow and Logic

if(build under current directory){ask if it is current directory, if it is run function}else{enter the main flow run function}/ / run function
function run(){
    if(Template path is the local file path){// Support local templates such as ds init /usr/webpack test
        if(Path exists){// Dynamically construct templates to your directory like test
            generate()
        }else{
            // Error log}}else{
        //1. Check the node version of the current process
        //2. Check the current package.json version and compare it with the remote repository version. If not, alert users to the new version
        //3. Download the remote repository to the local directory (usually in the. Ds-template folder of the user directory) and run generate}}Copy the code

Dynamic templates

The most important function is generate

  • If we remove the generate step, we can download a static template. If we don’t require customization, we can skip this step.
  • The generate function uses Metalsmith, the equivalent of gulp, to optimize the package by constantly writing middleware.
function generate(){
    const opts = getOptions(name, templatePath) as meta;  // Get meta. Js configuration and save it to opTS
    // We put the required files in the template directory of the source file, and other things like tests outside. Initialize metalsmith
    const metalsmith = Metalsmith(path.join(templatePath, 'template')) // We agreed to put all the template files in ds-cli-lib-template/template
    / / middleware
     metalsmith.use(askQuestions(opts.prompts))  Metalsmith.metadata ()
    .use(filterFiles(opts.filters))  // Filter out unwanted files by question interaction
    .use(renderTemplateFiles()); // The handlebar syntax can be used in the template as a placeholder, so here we re-render the template file

    // Package the source directory to the target directory to
    metalsmith.clean(false)
    .source('. ') 
    .destination(to)
    .build((err, files) = > {
      done(err);
    });
}
Copy the code
  • We need to construct meta-js in a template (such as ds-cli-lib-template) directory to customize the fields we need
module.exports={
    // These fields are stored by the middleware in metalsmith.metadata() for subsequent middleware calls
    prompts:{
        / / format you can refer to https://github.com/SBoudrias/Inquirer.js/#question
        name: {
            type: 'string'.required: true.message: 'Project name',},author: {
            type: 'string'.message: 'Author',},description: {
            type: 'string'.required: false.message: 'Project description'.default: 'Build a lib',},lint: {
            "type": "confirm"."message": "Whether to use tslint"}},filters: {
        Prompts. If prompts above are false, remove the file
        "tslint.json": "lint"."tsconfig.json": "lint"}}Copy the code

Now that we’ve covered the basics, you should be ready to put up scaffolding and templates that will suit your team.

Enumeration template repository

In general, there is a list command

Thanks to Github for providing us with the API, there are some interesting interfaces

  • Personal Information: api.github.com/users/ user name
  • All personal repo:api.github.com/users/ username/r…
  • Repo details :api.github.com/repos/ Username/warehouse…
  • The free list. api.github.com/repos/ Username/warehouse…
  • Details of an issue :api.github.com/repos/ User name/warehouse… Issues are numbered in the order 1,2, and 3.

const logSymbols = require('log-symbols');
const chalk = require('chalk');


export default async function(. args) {

    // Get the warehouse list
    const res=await request({
        url: 'https://api.github.com/users/yokiyokiyoki/repos'.method: 'GET'});let list;
    if(res.status===200) {
        console.log(logSymbols.info,chalk.green('There are the following templates'));
        list=res.data.filter((item) = > {
            return item.name.includes('ds-cli')&&item.name.includes('template');
        }).forEach(item= > {
            console.log();
            console.log(chalk.green(item.name));
        });
    } else {
        console.log(logSymbols.error,'Failed to get the warehouse list${res.data}`); }}Copy the code

Be aware that sometimes Github gets sloppy and requests are slow, which is not very user friendly (after trying the DS list a few times, developers will lose patience if it is slow).

If you are in pursuit of user experience, here is an idea: cache. You can write the result to a file, cache it in the local template folder (~/.ds-templates), and add the request time (mainly to determine whether it is today) to it, and then check in the code: When the next request, whether there is the original file, if there is, read it, after reading it to determine whether the time and the present time is one day apart, after one day to request again (time interval oneself grasp), overwrite the original cache file.

reference

  • Ds-cli: current temporary commands init and list (get all template list), contain detailed comments, do not understand you come!!
  • Ds-cli-lib-template: Build class libraries based on rollup and typescript
  • Ds-cli-doc-template: indicates a vuepress-based document template