Snippet, une extension pour VSCode et CLI. Partie 2





Bonne journée, mes amis!



Lors du développement du modèle de démarrage HTML moderne, j'ai pensé à étendre sa convivialité. À cette époque, les options pour son utilisation se limitaient au clonage du référentiel et au téléchargement de l'archive. C'est ainsi que l' extrait de code HTML et l'extension pour Microsoft Visual Studio Code - HTML Template , ainsi que l'interface de ligne de commande - create-modern-template sont apparus . Bien sûr, ces outils sont loin d'être parfaits et je vais les affiner autant que possible. Cependant, en les créant, j'ai appris quelques choses intéressantes que je souhaite partager avec vous.



L'extrait et l'expansion ont été traités dans la première partie... Dans cette partie, nous allons jeter un œil à la CLI.



Si vous n'êtes intéressé que par le code source, voici le lien vers le référentiel .



Oclif



Oclif est un framework Heroku pour créer des interfaces de ligne de commande.



Nous l'utiliserons pour créer une astuce qui offre la possibilité d'ajouter, de mettre à jour, de supprimer des tâches et d'afficher leur liste.



Le code source du projet est ici . Il existe également une CLI pour vérifier la fonctionnalité du site par URL.



Installez oclif globalement:



npm i -g oclif / yarn global add oclif

      
      





Oclif offre la possibilité de créer des CLI à commande unique et à commandes multiples. Nous avons besoin d'une deuxième option.



Nous créons un projet:



oclif multi todocli

      
      





  • l'argument multi indique Ă  oclif de crĂ©er une interface multi-commandes
  • todocli - nom du projet






Ajoutez les commandes nécessaires:



oclif command add
oclif command update
oclif command remove
oclif command show

      
      





Le fichier src / commandes / hello.js peut être supprimé.



Nous utiliserons lowdb comme base de données locale . Nous utiliserons également la craie pour personnaliser les messages affichés dans le terminal . Installez ces bibliothèques:



npm i chalk lowdb / yarn add chalk lowdb

      
      





Créez un fichier db.json vide dans le répertoire racine. Ce sera notre référentiel de tâches.



Dans le répertoire src, créez un fichier db.js avec le contenu suivant:



const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')
const adapter = new FileSync('db.json')
const db = low(adapter)

//   todos        db.json
db.defaults({ todos: [] }).write()

//    
const Todo = db.get('todos')

module.exports = Todo

      
      





Modification du fichier src / commandes / add.js:



//   
const { Command } = require('@oclif/command')
const Todo = require('../db')
const chalk = require('chalk')

class AddCommand extends Command {
  async run() {
    //     
    const { argv } = this.parse(AddCommand)
    try {
      //     
      await Todo.push({
        id: Todo.value().length,
        //       ,
        //  
        task: argv.join(' '),
        done: false
      }).write()
      //    
      this.log(chalk.green('New todo created.'))
    } catch {
      //    
      this.log(chalk.red('Operation failed.'))
    }
  }
}

//  
AddCommand.description = `Adds a new todo`

//      
AddCommand.strict = false

//  
module.exports = AddCommand

      
      





Modification du fichier src / commandes / update.js:



const { Command } = require('@oclif/command')
const Todo = require('../db')
const chalk = require('chalk')

class UpdateCommand extends Command {
  async run() {
    //   
    const { id } = this.parse(UpdateCommand).args
    try {
      //    id   
      await Todo.find({ id: parseInt(id, 10) })
        .assign({ done: true })
        .write()
      this.log(chalk.green('Todo updated.'))
    } catch {
      this.log('Operation failed.')
    }
  }
}

UpdateCommand.description = `Marks a task as done by id`

//     
UpdateCommand.args = [
  {
    name: 'id',
    description: 'todo id',
    required: true
  }
]

module.exports = UpdateCommand

      
      





Le fichier src / commandes / remove.js ressemble Ă  ceci:



const { Command } = require('@oclif/command')
const Todo = require('../db')
const chalk = require('chalk')

class RemoveCommand extends Command {
  async run() {
    const { id } = this.parse(RemoveCommand).args
    try {
      await Todo.remove({ id: parseInt(id, 10) }).write()
      this.log(chalk.green('Todo removed.'))
    } catch {
      this.log(chalk.red('Operation failed.'))
    }
  }
}

RemoveCommand.description = `Removes a task by id`

RemoveCommand.args = [
  {
    name: 'id',
    description: 'todo id',
    required: true
  }
]

module.exports = RemoveCommand

      
      





Enfin, Ă©ditez le fichier src / commands / show.js:



const { Command } = require('@oclif/command')
const Todo = require('../db')
const chalk = require('chalk')

class ShowCommand extends Command {
  async run() {
    //        id
    const res = await Todo.sortBy('id').value()
    //        
    //    
    if (res.length) {
      res.forEach(({ id, task, done }) => {
        this.log(
          `[${
            done ? chalk.green('DONE') : chalk.red('NOT DONE')
          }] id: ${chalk.yellowBright(id)}, task: ${chalk.yellowBright(task)}`
        )
      })
    //     
    } else {
      this.log('There are no todos.')
    }
  }
}

ShowCommand.description = `Shows existing tasks`

module.exports = ShowCommand

      
      





Étant dans le répertoire racine du projet, exécutez la commande suivante:



npm link / yarn link

      
      









Ensuite, nous effectuons plusieurs opérations.







Excellent. Tout fonctionne comme prévu. Il ne reste plus qu'à modifier package.json et README.md, et vous pouvez publier le package dans le registre npm.



CLI de bricolage



Notre CLI dans la fonctionnalité ressemblera à create-react-app ou vue-cli . Sur la commande create, il créera un projet dans le répertoire cible contenant tous les fichiers nécessaires au fonctionnement de l'application. De plus, il offrira la possibilité d'initialiser éventuellement git et d'installer des dépendances.



Le code source du projet est ici .



Créez un répertoire et initialisez le projet:



mkdir create-modern-template
cd create-modern-template
npm init -y / yarn init -y

      
      





Installez les bibliothèques requises:



yarn add arg chalk clear esm execa figlet inquirer listr ncp pkg-install

      
      







Dans le répertoire racine, créez un fichier bin / create (sans extension) avec le contenu suivant:



#!/usr/bin/env node

require = require('esm')(module)

require('../src/cli').cli(process.argv)

      
      





Modification de package.json:



"main": "src/main.js",
"bin": "bin/create"

      
      





La commande create est enregistrée.



Créez un répertoire src / template et placez-y les fichiers du projet, qui seront copiés dans le répertoire cible.



Créez un fichier src / cli.js avec le contenu suivant:



//   
import arg from 'arg'
import inquirer from 'inquirer'
import { createProject } from './main'

//    
// --yes  -y    git   
// --git  -g   git
// --install  -i   
const parseArgumentsIntoOptions = (rawArgs) => {
  const args = arg(
    {
      '--yes': Boolean,
      '--git': Boolean,
      '--install': Boolean,
      '-y': '--yes',
      '-g': '--git',
      '-i': '--install'
    },
    {
      argv: rawArgs.slice(2)
    }
  )

  //    
  return {
    template: 'template',
    skipPrompts: args['--yes'] || false,
    git: args['--git'] || false,
    install: args['--install'] || false
  }
}

//   
const promptForMissingOptions = async (options) => {
  //     --yes  -y
  if (options.skipPrompts) {
    return {
      ...options,
      git: false,
      install: false
    }
  }

  // 
  const questions = []

  //      git
  if (!options.git) {
    questions.push({
      type: 'confirm',
      name: 'git',
      message: 'Would you like to initialize git?',
      default: false
    })
  }

  //      
  if (!options.install) {
    questions.push({
      type: 'confirm',
      name: 'install',
      message: 'Would you like to install dependencies?',
      default: false
    })
  }

  //   
  const answers = await inquirer.prompt(questions)

  //    
  return {
    ...options,
    git: options.git || answers.git,
    install: options.install || answers.install
  }
}

//        
export async function cli(args) {
  let options = parseArgumentsIntoOptions(args)

  options = await promptForMissingOptions(options)

  await createProject(options)
}

      
      





Le fichier src / main.js ressemble Ă  ceci:



//   
import path from 'path'
import chalk from 'chalk'
import execa from 'execa'
import fs from 'fs'
import Listr from 'listr'
import ncp from 'ncp'
import { projectInstall } from 'pkg-install'
import { promisify } from 'util'
import clear from 'clear'
import figlet from 'figlet'

//        
const access = promisify(fs.access)
const copy = promisify(ncp)

//  
clear()

//     HTML - 
console.log(
  chalk.yellowBright(figlet.textSync('HTML', { horizontalLayout: 'full' }))
)

//   
const copyFiles = async (options) => {
  try {
    // templateDirectory -    ,
    // targetDirectory -  
    await copy(options.templateDirectory, options.targetDirectory)
  } catch {
    //    
    console.error('%s Failed to copy files', chalk.red.bold('ERROR'))
    process.exit(1)
  }
}

//   git
const initGit = async (options) => {
  try {
    await execa('git', ['init'], {
      cwd: options.targetDirectory,
    })
  } catch {
    //    
    console.error('%s Failed to initialize git', chalk.red.bold('ERROR'))
    process.exit(1)
  }
}

//   
export const createProject = async (options) => {
  //     
  options.targetDirectory = process.cwd()

  //     
  const fullPath = path.resolve(__filename)

  //       
  const templateDir = fullPath.replace('main.js', `${options.template}`)

  options.templateDirectory = templateDir

  try {
    //     
    //  R_OK -    
    await access(options.templateDirectory, fs.constants.R_OK)
  } catch {
    //    
    console.error('%s Invalid template name', chalk.red.bold('ERROR'))
    process.exit(1)
  }

  //   
  const tasks = new Listr(
    [
      {
        title: 'Copy project files',
        task: () => copyFiles(options),
      },
      {
        title: 'Initialize git',
        task: () => initGit(options),
        enabled: () => options.git,
      },
      {
        title: 'Install dependencies',
        task: () =>
          projectInstall({
            cwd: options.targetDirectory,
          }),
        enabled: () => options.install,
      },
    ],
    {
      exitOnError: false,
    }
  )

  //  
  await tasks.run()

  //    
  console.log('%s Project ready', chalk.green.bold('DONE'))

  return true
}

      
      





Nous connectons la CLI (étant dans le répertoire racine):



yarn link

      
      





Créez un répertoire cible et un projet:



mkdir test-dir
cd test-dir
create-modern-template && code .

      
      













Ă€ la perfection. CLI prĂŞt Ă  publier.



Publication d'un package dans le registre npm



Pour pouvoir publier des packages, vous devez d'abord créer un compte dans le registre npm .



Ensuite, vous devez vous connecter en exécutant la commande de connexion npm et en spécifiant votre adresse e-mail et votre mot de passe.



Ensuite, nous éditons package.json et créons les fichiers .gitignore, .npmignore, LICENSE et README.md (voir le référentiel du projet).



Nous empaquetons les fichiers de projet à l'aide de la commande npm package. Nous obtenons le fichier create-modern-template.tgz. Nous publions ce fichier en exécutant la commande npm publish create-modern-template.tgz.



Obtenir une erreur lors de la publication d'un package signifie généralement qu'un package portant ce nom existe déjà dans le registre npm. Pour mettre à jour un package, vous devez modifier la version du projet dans package.json, créer à nouveau le fichier TGZ et l'envoyer pour publication.



Une fois qu'un paquet a été publié, il peut être installé comme n'importe quel autre paquet en utilisant npm i / yarn add.







Comme vous pouvez le voir, la création de l'interface de ligne de commande et la publication du package dans le registre npm sont simples.



J'espère que vous avez trouvé quelque chose d'intéressant pour vous-même. Merci de votre attention.



All Articles