Snippet、VSCodeおよびCLIの拡張。パート2





良い一日、友達! 最新のHTMLスターターテンプレート



を開発して いるときに、その使いやすさを拡張することを考えました。当時、その使用オプションは、リポジトリのクローン作成とアーカイブのダウンロードに限定されていました。これは、Microsoft Visual StudioコードHTMLスニペットと拡張機能( HTMLテンプレート)、およびコマンドラインインターフェイス( create-modern-template)がどのように表示されたかを示してい ます。もちろん、これらのツールは完璧にはほど遠いので、できる限り改良していきます。しかし、それらを作成する過程で、私はあなたと共有したいいくつかの興味深いことを学びました。



スニペットと拡張については、 最初の部分で説明しましたこのパートでは、CLIについて見ていきます。



ソースコードのみに関心がある場合は、 ここにリポジトリへのリンクがあります



オクリフ



Oclifは、コマンドラインインターフェイスを構築するためのHerokuフレームワークです。



これを使用して、タスクを追加、更新、削除し、それらのリストを表示する機能を提供するトリックを作成します。



プロジェクトのソースコードは こちらです。URLでサイトの機能をチェックするためのCLIもあります。



oclifをグローバルにインストールします。



npm i -g oclif / yarn global add oclif

      
      





Oclifは、シングルコマンドCLIとマルチコマンドCLIの両方を作成する機能を提供します。2番目のオプションが必要です。



プロジェクトを作成します。



oclif multi todocli

      
      





  • multi引数は、oclifにマルチコマンドインターフェイスを作成するように指示します
  • todocli-プロジェクト名






必要なコマンドを追加します。



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

      
      





src /コマンド/hello.jsファイルは削除できます。



ローカルデータベースとしてlowdbを使用します また、チョークを使用して、端末に表示されるメッセージをカスタマイズします これらのライブラリをインストールします。



npm i chalk lowdb / yarn add chalk lowdb

      
      





ルートディレクトリに空のdb.jsonファイルを作成します。これがタスクリポジトリになります。



srcディレクトリに、次の内容のdb.jsファイルを作成します。



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

      
      





src / messages / 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

      
      





src / messages / 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

      
      





src /コマンド/remove.jsファイルは次のようになります。



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

      
      





最後に、src /コマンド/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

      
      





プロジェクトのルートディレクトリにいるときに、次のコマンドを実行します。



npm link / yarn link

      
      









次に、いくつかの操作を実行します。







結構です。すべてが期待どおりに機能します。残っているのはpackage.jsonとREADME.mdを編集することだけで、パッケージをnpmレジストリに公開できます。



DIY CLI



CLIの機能は、create-react-appまたは vue-cliに似てい ますcreateコマンドでは、アプリケーションが機能するために必要なすべてのファイルを含むプロジェクトをターゲットディレクトリに作成します。さらに、オプションでgitを初期化し、依存関係をインストールする機能を提供します。



プロジェクトのソースコードは こちらです。



ディレクトリを作成し、プロジェクトを初期化します。



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

      
      





必要なライブラリをインストールします。



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

      
      





  • arg-コマンドライン引数を解析するためのツール
  • clear-端末をクリアするためのツール
  • esmは、Node.jsでES6モジュールのサポートを提供するツールです。
  • execaは、いくつかの一般的な操作を自動的に実行するためのツールです(gitを初期化するために使用します)
  • figlet-カスタマイズされたテキストを端末に出力するためのツール
  • inquirer-コマンドラインを操作するためのツール。特に、ユーザーに質問したり、ユーザーの回答を解析したりすることができます。
  • listr-タスクのリストを作成し、ターミナルでの実行を視覚化するためのツール
  • ncp-ファイルとディレクトリをコピーするためのツール
  • pkg-install-プロジェクトの依存関係をプログラムでインストールするためのツール


ルートディレクトリで、次の内容のbin / createファイル(拡張子なし)を作成します。



#!/usr/bin/env node

require = require('esm')(module)

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

      
      





package.jsonの編集:



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

      
      





createコマンドが登録されています。



src / templateディレクトリを作成し、そこにプロジェクトファイルを配置します。これは、ターゲットディレクトリにコピーされます。



次の内容でsrc / cli.jsファイルを作成します。



//   
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)
}

      
      





src /main.jsファイルは次のようになります。



//   
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
}

      
      





CLIを接続します(ルートディレクトリにあります)。



yarn link

      
      





ターゲットディレクトリとプロジェクトを作成します。



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

      
      













完璧に。CLIを公開する準備ができました。



npmレジストリへのパッケージの公開



パッケージを公開できるようにするには、最初にnpmレジストリにアカウントを作成する必要があり ます



次に、npm loginコマンドを実行し、電子メールとパスワードを指定してログインする必要があります。



その後、package.jsonを編集し、.gitignore、.npmignore、LICENSE、およびREADME.mdファイルを作成します(プロジェクトリポジトリを参照)。



npmpackageコマンドを使用してプロジェクトファイルをパッケージ化します。 create-modern-template.tgzファイルを取得します。コマンドnpmpublish create-modern-template.tgzを実行して、このファイルを公開します。



パッケージの公開中にエラーが発生するということは、通常、同じ名前のパッケージがnpmレジストリにすでに存在していることを意味します。パッケージを更新するには、package.jsonでプロジェクトのバージョンを変更し、TGZファイルを再度作成して、公開するために送信する必要があります。



パッケージが公開されると、npm i / yenaddを使用して他のパッケージと同じようにインストールできます。







ご覧のとおり、CLIを作成してパッケージをnpmレジストリに公開するのは簡単です。



何か面白いものを見つけていただければ幸いです。清聴ありがとうございました。



All Articles