origin

It’s common to see articles that begin with a picture of a word on a solid color background, like the one above, to outline the topic of the article. I would like to such a simple picture can be quickly generated by some tools? I know canvas can quickly draw text and generate pictures, requirements are easy to achieve. So what are the tools that carry it? The command line seems like a good choice, taking the user’s input, controlling the image style with a few extra parameters, generating the image and downloading it to the user’s hard drive. After a bit of practice, it worked, which led to this article detailing how to write a Node command-line tool from scratch.

Initialize the

Initialize the project

mkdir word2img-cli
cd word2img-cli
npm init
Copy the code

Package. json needs to be changed. The main and scripts fields are removed because they are not needed, and the bin field is added to specify the executable path. The diff code of the markdown file is highlighted here to mark file changes, and will be used throughout the rest of the diff file.

{" name ":" word2img - cli ", "version" : "0.0.1", "description" : "command line interface for generate image based on your input",- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
-},
+ "bin": {
+ "word2img": "bin/word2img.js"
+}
  }
Copy the code

Create the bin/word2img.js file and enter the following information

#! /usr/bin/env node console.log('hello world')Copy the code

Now type./bin/word2img.js on the command line and you should see normal output

But now you need to enter the relative path to the file, which is not cool, so let’s get rid of the relative path, and run the command NPM link

At this point directly in the command line input command word2img, you can also get the same output as just now, good, you can not write path directly input commands, there is an inside flavor.

At this point, a few concepts introduced above need to be explained

The bin field of package.json

The bin field is used to map the path of an executable file. It is similar to adding an environment variable. For details, see official documents

start-of-the-file#! /usr/bin/env node

To indicate to the system that the interpreter for this file is node, refer to Shebang

npm link

As you can see from the screenshot of the NPM Link execution output above, the current project is soft-chained to the node_modules folder in the nodejs installation directory, and then soft-chained to the executable file of the same name in the nodeJS installation directory.

Take my current project as an example, I use Windows system, open C:\Program Files\nodejs\ folder, you can see more than two Files word2img and word2img.cmd, these two Files can be opened with the editor, here do not stick, interested can operate under. At the same time, C:\Program Files\nodejs\node_modules\ folder has a word2img-cli soft connection folder with the same content as our project.

After executing this command, you can use our global command, NPM link can also specify the project to use, here will not expand, for detailed usage or refer to the official documentation.

On Commander and Chalk

Now we can only type a command and output a Hello world, which seems to be completely useless. Let’s review some of the common command lines, using the common @vue/cli as an example:

View the current version
vue -V # or vue - version

# View the instructions
vue -h # or vue -- help

# New project
vue create vue-demo

# Use option to specify a new preset project
vue create -p vue3 vue3-demo

# Add esLint plugin
vue add eslint

Add the esLint plugin and specify plug-in options
vue add eslint --config airbnb --lintOn save
Copy the code

There are the most common view version and view instructions, this is a more general function. In vue create vue-demo, create is a command, and vue-demo is a command value. In vue creation-p vue3 vue3-demo, -p is option, and vue3 is the value of option p.

You can also specify different options in different locations, Vue add eslint –config airbnb –lintOn save –config airbnb –lintOn Save is the pluginOptions passed to esLint. You can also verify this by typing vue -h to see vue add [options] [pluginOptions].

Run vue –help for detailed usage of given command. Run vue –help for detailed usage of given command.

commander

Next, start implementing these similar effects. We use native nodejs can also deal with command line parameters, but more troublesome, [commander] (https://www.npmjs.com/package/commander) is a mature solution.

npm install --save commander
Copy the code

/bin/word2img.js file:

#! /usr/bin/env node- console.log('hello world')
+ const program = require('commander')
+ program.version(require('.. /package').version).usage(' [options]')
+ program.parse(process.argv)
Copy the code

At this point, type word2img -v (capital here, or –version) and -h(or –help), respectively, and you can see that the view version and view instructions are complete. Comparing the map below with the map of vue command above, it is very close. Commander helped us to complete all this, and it only needs three lines of code. The code is very simple, so I will not explain it.

chalk

Going back to the output map of vue –help above, the last sentence is Run vue –help for detailed usage of given command. The color of vue –help is cyan. To color the output from the command line, we can use Chalk

npm install --save chalk
Copy the code

/bin/word2img.js file:

#! /usr/bin/env node const program = require('commander')+ const chalk = require('chalk')
+ program.version(require('.. /package').version).usage('<command> [options]')+ 
+ program.on('--help', () => {
+ console.log()
+ console.log(
+ ` Run ${chalk.cyan(
+ `word2img  --help`
+ )} for detailed usage of given command.`
+)
+ console.log()
+})
+ 
  program.parse(process.argv)
Copy the code

At this point, enter word2img -h on the command line, and you can already see the colored output

It can be seen that chalk is very simple to use, and it provides many built-in colors. For more information about the usage of message, you can check the official website.

Define the first command

Going back to the first image in the article, if our requirement is to take a string input from the user and turn it into an image with text in the middle of a solid color background, then we can roughly determine that the external variables we need are:

  • Word (string to be converted to picture)
  • Width of the image
  • Height of the image
  • BgColor (background color of the image)
  • Color (text color)
  • Word-wrap: break-word! Important;
  • > > < span style = “box-sizing: border-box; color: RGB (50, 50, 50);
  • Filename (filename of the image to download)
  • Filepath (where the image is saved)

In which, word is the value of the command itself, and the rest are all options. If we consider only the variables mentioned above, we can write the following code to modify: /bin/word2img.js:

// program. Version (...)+ program
+ .command('new 
      
       ')
      
+ .description('generate a new image use the input word')
+ .option('-w --width 
      
       ', 'Set width of the image', 600)
      
+ .option('--height 
      
       ', 'Set geight of the image', 200)
      
+ .option('--bgColor 
      
       ', 'Set backgound-color of the image', '#fff')
      
+ .option('--color 
      
       ', 'Set color of the word', '#000')
      
+ .option('--size 
      
       ', 'Set font-size of the word', 48)
      
+ .option('--family 
      
       ', 'Set font-family of the word', 'Arial')
      
+ .option('--filename 
      
       ', 'Set filename of the image')
      
+ .option('--filepath 
      
       ', 'Set file path to save the image(note that the path doesn\'t contain the file name)', process.cwd())
      
+ .action((word, options) => {
+ console.log(word, options)
+ // Canvas draws and generates images...
+})// program. On (...)Copy the code

Enter word2img new ‘hello world’ and you get the following output:

Explain the code and output above: first, the program.command method receives a string new

, which defines a command where new is the name of the command and

refers to the value of the command. The description method then receives a string that briefly describes what the command does.

The following is a series of options methods that define the options of the command, which correspond exactly to the variables mentioned above. The first argument to this method is similar to the command, defining the option name (note that the option is abbreviated with a one-letter dash -w, and followed by an unlimited number of characters with two slashes –width). The second argument is a string describing what the option does. If you want to provide a default value for this option, you can specify it through the third option.

Finally, the action method listens for user input. When the user enters the new command, the callback function is triggered. The first argument of the callback function is the value of the command, and the second argument is the option object above. Subsequent specific business code can be added in this callback function.

Implement canvas related business code

The focus of this article is actually on the command line, so the business code will not be detailed, directly on the code:

npm install --save canvas
Copy the code

Create./utils/ newcanvas.js with the following content:

const { createCanvas } = require('canvas')

exports.newCanvas = function (word, options) {
  const canvas = createCanvas(options.width, options.height)
  const ctx = canvas.getContext('2d')

  // rect
  ctx.fillStyle = options.bgColor
  ctx.fillRect(0.0, options.width, options.height)
  // word
  ctx.textBaseline = 'middle'
  ctx.textAlign = 'center'
  ctx.font = `${options.size}px ${options.family}`
  ctx.fillStyle = options.color
  ctx.fillText(word, options.width / 2, options.height / 2)

  return {
    canvas,
    ctx,
  }
}
Copy the code

/utils/canvas2img.js

const fs = require('fs')
const path = require('path')
const chalk = require('chalk')

exports.canvas2img = function (canvas, filename, filepath) {
  const buf = canvas.toBuffer()
  filename = filename || `word2img_The ${Date.now()}.png`
  const url = path.resolve(filepath, filename)
  fs.writeFile(url, buf, function (err) {
    if (err) {
      console.log(err)
    } else {
      console.log(
        '✨ image generated successfully at:${chalk.yellow( url )}`)}}}Copy the code

The contents of the./bin/word2img.js file are modified as follows:

#! /usr/bin/env node const program = require('commander') const chalk = require('chalk')+ const { newCanvas } = require('.. /utils/newCanvas')
+ const { canvas2img } = require('.. /utils/canvas2img')program.version(require('.. /package').version).usage('<command> [options]') program .command('new <word>') .description('generate a new image use the input word') .option('-w --width <width>', 'Set width of the image', 600) .option('--height <height>', 'Set geight of the image', 200) .option('--bgColor <bgColor>', 'Set backgound-color of the image', '#fff') .option('--color <color>', 'Set color of the word', '#000') .option('--size <size>', 'Set font-size of the word', 48) .option('--family <family>', 'Set font-family of the word', 'Arial') .option('--filename <filename>', 'Set filename of the image') .option('--filepath <filepath>', 'Set file path to save the image(note that the path doesn\'t contain the file name)', process.cwd()) .action((word, options) => {- console.log(word)
- console.log(options)
+ const { canvas } = newCanvas(word, options)
+ canvas2img(canvas, options.filename, options.filepath)
    })
  
  program.on('--help', () => {
    console.log()
    console.log(
      `  Run ${chalk.cyan(
        `word2img <command> --help`
      )} for detailed usage of given command.`
    )
    console.log()
  })
  
  program.parse(process.argv)
Copy the code

At this point, enter word2img new ‘hell-word’ command line, successfully generated in the project root directory, and the terminal will prompt the location of the file information. Of course, you can also pass the different options listed above, so I won’t list them all here.

At this point, a simple command line is complete, basically to achieve the requirements at the beginning of the article. In fact, there are a lot of things that can be optimized. For example, you can ask a command line question and let the user type an answer or select an answer to simplify the long choice typing and improve the user experience, which should be familiar to anyone familiar with @vue/ CLI. Due to the length and complexity of the code, this part will not be introduced in this article. Those who are interested can view the source code of the project, or directly see the source code of @vue/cli. I read the source code of @vue/cli before writing this project, and got a lot of help.

Release NPM package

Enter the user name and password to login to NPM whoami. After the login is successful, NPM whoami can see its user name:

npm login
npm whoami
Copy the code

If the NPM uses the Taobao source, switch to the official source. You are advised to use NRM to manage the NPM source

npm install -g nrm
nrm ls
nrm use npm
Copy the code

And then publish, and if you publish prompt 403, it’s probably the same package name or the email is not authenticated, just go to the email and click authentication

npm publish
Copy the code

After the successful release of NPM, go to the official website to search word2img-cli, you can see our package, MY side has been released, interested partners can install the next play.