Based on Webpack 4.x project actual combat 1- simple use
Based on Webpack 4.x project actual combat 2 – configuration once, multiple projects run
Based on Webpack 4.x project combat 3 – handwriting a CLI
preface
Having written about webpack-Multi configuration, CSS preprocessor uses less, today we will continue this series of articles by writing a CLI tool. So this article isn’t really webpack, it’s about writing your own CLI tool, but it’s based on the previous two articles, so it’s in this series.
Let’s refer to vue-CLI to learn how to write cli tools.
Let’s take a look at vue-CLI:
npm install -g vue-cli
vue init webpack test
Test is your project name and then there will be some configuration questions and answers
Project name test: Project name, if not changed, the originaltestName Project Description A vue. js Project: indicates the Project description. Alternatively, click Enter and use the default name Author: Author Vue build standalone: Install vue-router? Install vue-router Use ESLint to lint your code? Pick an ESLint management code Pick an ESLint Standard: Choose an ESLint preset, the code style for writing vue projects Set up unit tests Yes: Install unit tests Pick atestrunner jest Setup e2e tests with Nightwatch? Should we run NPM installforyou after the project has been created? (recommended) NPM: Whether to help you 'NPM install'Copy the code
Referring to vue-CLI, our scaffolding is called Webpack-multi-CLI and is built based on webpack4.x project Live 2 – configuration once, multiple projects running this demo.
When done, our command is similar to vue-CLI
- The installation
npm install -g webpack-multi-cli
- use
npm init project-name
, the following configuration choices appear
Project name: Project description A webpack-Multi Project: Project description A webpack-multi Project: Author Pick a CSS preprocessor? Select a CSS preprocessor. The value can be less or sass Should we run NPM installforyou after the project has been created? (recommended) NPM: whether to help you 'NPM install', if you enter NPM command, then help you to execute NPM installCopy the code
Begin to build
Before we can start building, we need to install some NPM packages:
Chalk: color output figlet: generate character pattern inquirer: Inquirer command line user interface commander Custom command fs-extra file operation ORA Make loop effect Promise-exec execute subprocess
Write a package.json file
Take a look at our final package.json file
{
"name": "webpack-multi-cli"."version": "1.0.0"."description": "create webpack-multi cli"."main": "index.js"."bin": {
"webpack-multi-cli": "bin/index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git"."url": "git+https://github.com/xianyulaodi/webpack-multi-cli.git"
},
"author": "xianyulaodi"."license": "MIT"."bugs": {
"url": "https://github.com/xianyulaodi/webpack-multi-cli/issues"
},
"dependencies": {
"chalk": "^" 2.4.2."commander": "^ 2.20.0"."figlet": "^ 1.2.1." "."fs-extra": "^" 7.0.1."inquirer": "^ 5.2.0." "."ora": "^ 3.4.0"."promise-exec": "^ 0.1.0 from"
},
"homepage": "https://github.com/xianyulaodi/webpack-multi-cli#readme"
}
Copy the code
Our entry file is bin/index.js, which is similar to vue-cli init project name. Our initial command is webpack-multi-cli init project name, so the bin of package.json needs to be written like this
."bin": {
"webpack-multi-cli": "bin/index.js"}..Copy the code
Writing the init command
Next, write a custom init command that relies on the commander library, lib/cmd.js
#! /usr/bin/env node
const program = require("commander");
const path = require('path');
const currentPath = process.cwd(); // Current directory path
const fs = require('fs-extra');
let projectName = null;
// Define directives
program
.version(require('.. /package.json').version)
.command('init <name>')
.action(function (name) {
projectName = name;
});
program.parse(process.argv);
program.projectName = projectName;
if(! projectName) {console.error('no project name was given, eg: webpack-multi-cli init myProject');
process.exit(1);
}
if (fs.pathExistsSync(path.resolve(currentPath, `${projectName}`))) {
console.error('The name of the project you created:${projectName}It already exists, failed to create, please modify the project name and try again);
process.exit(1);
}
module.exports = program;
Copy the code
If no project name is passed in, or if one already exists, an exception is thrown and the program ends, saving the project name as a default name. Remember our orders? No,
Project name test:// The project name is the same as the original test name
Copy the code
This is where the default project name comes from
Writing interaction questions
This relies on the inquirer library
lib/inquirer.ja
const inquirer = require('inquirer');
const cmd = require('./cmd');
const projectName = cmd.projectName;
module.exports = {
getQuestions: (a)= > {
const questions = [
{
name: 'projectName'.type: 'input'.message: 'Project name'.default: projectName
},
{
name: 'description'.type: 'input'.message: `Project description`.default: 'A webpack-multi project'
},
{
name: 'author'.type: 'input'.message: `Author`}, {name: 'cssPreprocessor'.type: 'list'.message: 'Pick an css preprocessor'.choices: [
"less"."sass"] {},name: 'isNpmInstall'.type: 'input'.message: 'Should we run `npm install` for you after the project has been create?
'
,}];returninquirer.prompt(questions); }},Copy the code
The main is some of our interaction questions, the specific use of this library, you can Google
Master file
bin/index.js
The idea here is to create a new template directory to store our WebPack configuration template, write the questions you entered, such as project name, author, description, choose less or sass, etc., into these configuration files, and then download them to the root directory where you executed the command
Another idea is to grab the interaction problem you came back to, grab the file from Github, and download it to the root directory where you executed the command
Once you know the idea, it’s a little bit easier
Start by getting the current path where you typed the command
const currentPath = process.cwd(); // Current directory path
Copy the code
Interaction problem to get input
const config = await inquirer.getQuestions();
Copy the code
The next step is to write the answers to the interaction questions into our template
- Write package. Json
function handlePackageJson(config) {
const spinner = ora('Writing package.json... ').start();
const promise = new Promise((resolve, reject) = > {
const packageJsonPath = path.resolve(`${currentPath}/${config.projectName}`.'package.json');
fs.readJson(packageJsonPath, (err, json) => {
if (err) {
console.error(err);
}
json.name = config.projectName;
json.description = config.description;
json.author = config.author;
if(config.cssPreprocessor == 'less') {
json.devDependencies = Object.assign(json.devDependencies, {
"less": "^ 3.9.0"."less-loader": "^ 4.1.0." "
});
} else {
json.devDependencies = Object.assign(json.devDependencies, {
"sass-loader": "^ 7.1.0"."node-sass": "^ 4.11.0"
});
}
fs.writeJSON(path.resolve(`${currentPath}/${config.projectName}/package.json`), json, err => {
if (err) {
return console.error(err)
}
spinner.stop();
ora(chalk.green('Package. json write success')).succeed();
resolve();
});
});
});
return promise;
}
Copy the code
- Write the WebPack configuration
The interaction problem with Webpack is simple: choose less or sass, which defaults to less
function handleWebpackBase(config) {
const spinner = ora('Writing to webpack... ').start();
const promise = new Promise((resolve, reject) = > {
const webpackBasePath = path.resolve(`${currentPath}/${config.projectName}`.'_webpack/webpack.base.conf.js');
fs.readFile(webpackBasePath, 'utf8'.function(err, data) {
if (err) {
return console.error(err)
}
if(config.cssPreprocessor == 'scss') {
data = data.replace("less-loader"."sass-loader");
}
fs.writeFile(path.resolve(`${currentPath}/${config.projectName}/_webpack/webpack.base.conf.js`), data, (err,result) => {
if (err) {
return console.error(err)
}
spinner.stop();
ora(chalk.green('Webpack write successful')).succeed(); resolve(); })})});return promise;
}
Copy the code
The complete main file bin/index.js
#! /usr/bin/env node
const inquirer = require('.. /lib/inquirer');
const path = require('path');
const fs = require('fs-extra');
const ora = require('ora'); // The terminal displays the wheel loading
const chalk = require('chalk');
const figlet = require('figlet');
const exec = require('promise-exec');
const currentPath = process.cwd(); // Current directory path
const templatePath = path.resolve(__dirname, '.. /template\/');
function handlePackageJson(config) {
const spinner = ora('Writing package.json... ').start();
const promise = new Promise((resolve, reject) = > {
const packageJsonPath = path.resolve(`${currentPath}/${config.projectName}`.'package.json');
fs.readJson(packageJsonPath, (err, json) => {
if (err) {
console.error(err);
}
json.name = config.projectName;
json.description = config.description;
json.author = config.author;
if(config.cssPreprocessor == 'less') {
json.devDependencies = Object.assign(json.devDependencies, {
"less": "^ 3.9.0"."less-loader": "^ 4.1.0." "
});
} else {
json.devDependencies = Object.assign(json.devDependencies, {
"sass-loader": "^ 7.1.0"."node-sass": "^ 4.11.0"
});
}
fs.writeJSON(path.resolve(`${currentPath}/${config.projectName}/package.json`), json, err => {
if (err) {
return console.error(err)
}
spinner.stop();
ora(chalk.green('Package. json write success')).succeed();
resolve();
});
});
});
return promise;
}
function handleWebpackBase(config) {
const spinner = ora('Writing to webpack... ').start();
const promise = new Promise((resolve, reject) = > {
const webpackBasePath = path.resolve(`${currentPath}/${config.projectName}`.'_webpack/webpack.base.conf.js');
fs.readFile(webpackBasePath, 'utf8'.function(err, data) {
if (err) {
return console.error(err)
}
if(config.cssPreprocessor == 'scss') {
data = data.replace("less-loader"."sass-loader");
}
fs.writeFile(path.resolve(`${currentPath}/${config.projectName}/_webpack/webpack.base.conf.js`), data, (err,result) => {
if (err) {
return console.error(err)
}
spinner.stop();
ora(chalk.green('Webpack write successful')).succeed(); resolve(); })})});return promise;
}
function successConsole(config) {
console.log(' ');
const projectName = config.projectName;
console.log(`${chalk.gray('Project path:')} ${path.resolve(`${currentPath}/${projectName}`)}`);
console.log(chalk.gray('Next, execute:'));
console.log(' ');
console.log(' ' + chalk.green('cd ') + projectName);
if(config.isNpmInstall ! ='npm') {
console.log(' ' + chalk.green('npm install'));
}
console.log(' ' + chalk.green('npm run dev --dirname=demo'));
console.log(' ');
console.log(chalk.green('enjoy coding ... '));
console.log(
chalk.green(figlet.textSync("webpack multi cli"))); }function createTemplate(config) {
const projectName = config.projectName;
const spinner = ora('Generating... ').start();
fs.copy(path.resolve(templatePath), path.resolve(`${currentPath}/${projectName}`))
.then((a)= > {
spinner.stop();
ora(chalk.green('Directory generated successfully! ')).succeed();
return handlePackageJson(config);
})
.then((a)= > {
return handleWebpackBase(config);
})
.then((a)= > {
if(config.isNpmInstall == 'npm') {
const spinnerInstall = ora('Install dependencies in... ').start();
if(config.cssPreprocessor == 'sass') {
console.log('if the node - sass installation failed, please check: https://github.com/sass/node-sass');
}
exec('npm install', {
cwd: `${currentPath}/${projectName}`
}).then(function(){
console.log(' ')
spinnerInstall.stop();
ora(chalk.green('Phase-dependent installation successful! ')).succeed();
successConsole(config);
}).catch(function(err) {
console.error(err);
});
} else {
successConsole(config);
}
})
.catch(err= > console.error(err))
}
const launch = async() = > {const config = await inquirer.getQuestions();
createTemplate(config);
}
launch();
Copy the code
Finally, we can publish our scaffolding to NPM. About NPM package publishing, we can click to see how to write a NPM package
Our package has been released successful www.npmjs.com/package/web…
NPM install webpack-multi-CLI-g
Use webpack Init myTest as shown below:
After installing the dependencies, run NPM run dev –dirname=demo to see what happens
At this point, our Webpack-multi-CLI scaffolding is complete. It is relatively simple, certainly not comparable to vue-CLI, but the basic idea is there. If you want to do more complicated scaffolding, it is just an extension.
Through this article, I hope you can learn how to write a scaffolding of your own, understand the basic ideas similar to vue-CLI, hope you can have some harvest
Click here for the source code