Snippet, eine Erweiterung für VSCode und CLI. Teil 1





Guten Tag, Freunde!



Bei der Entwicklung der Modern HTML Starter Template habe ich darüber nachgedacht, die Benutzerfreundlichkeit zu erweitern. Zu diesem Zeitpunkt beschränkten sich die Optionen für die Verwendung auf das Klonen des Repositorys und das Herunterladen des Archivs. Auf diese Weise wurden das HTML-Snippet und die Erweiterung für Microsoft Visual Studio-Code - HTML-Vorlage sowie die Befehlszeilenschnittstelle - create-modern-template angezeigt . Natürlich sind diese Werkzeuge alles andere als perfekt und ich werde sie so weit wie möglich verfeinern. Während der Erstellung habe ich jedoch einige interessante Dinge gelernt, die ich mit Ihnen teilen möchte.



In diesem Teil werden wir uns Snippet und Erweiterung und im nächsten Teil CLI ansehen.



Wenn Sie nur am Quellcode interessiert sind, finden Sie hier den Link zum Repository .



Snippet



Was ist ein Ausschnitt? Kurz gesagt, ein Snippet ist eine Vorlage, die der Editor für die automatische Vervollständigung (Code-Vervollständigung) verwendet.



VSCode verfügt über ein integriertes Emmet ( offizielle Website , Emmet in Visual Studio Code ), das zahlreiche HTML-, CSS- und JS-Snippets verwendet, um Ihnen beim Schreiben Ihres Codes zu helfen. Wir geben den Editor (in .html) ein! Drücken Sie die Tabulatortaste oder die Eingabetaste, um das fertige HTML5-Markup zu erhalten. Wir geben nav> ul> li * 3> a.link> img ein, drücken die Tabulatortaste, wir erhalten:



<nav>
    <ul>
      <li><a href="" class="link"><img src="" alt=""></a></li>
      <li><a href="" class="link"><img src="" alt=""></a></li>
      <li><a href="" class="link"><img src="" alt=""></a></li>
    </ul>
  </nav>

      
      





usw.



Zusätzlich zu den integrierten bietet VSCode die Möglichkeit, benutzerdefinierte Snippets zu verwenden. Um sie zu erstellen, gehen Sie zu Datei -> Einstellungen -> Benutzer-Snippets (oder klicken Sie auf die Schaltfläche Verwalten in der unteren linken Ecke und wählen Sie Benutzer-Snippets). Die Einstellungen für jede Sprache werden in einer entsprechenden JSON-Datei gespeichert (für HTML in html.json, für JavaScript in javascript.json usw.).



Lassen Sie uns das Erstellen von JS-Snippets üben. Suchen Sie die Datei javascript.json und öffnen Sie sie.







Wir sehen Kommentare, die kurz die Regeln zum Erstellen von Snippets beschreiben. Weitere Informationen zum Erstellen benutzerdefinierter Snippets in VSCode finden Sie hier .



Beginnen wir mit etwas Einfachem. Erstellen wir ein Snippet für console.log (). So sieht es aus:



"Print to console": {
  "prefix": "log",
  "body": "console.log($0)",
  "description": "Create console.log()"
},

      
      





  • Auf Konsole drucken - Objektschlüssel, Snippetname (erforderlich)
  • Präfix - Abkürzung für Snippet (erforderlich)
  • Körper - das Snippet selbst (erforderlich)
  • $ number - Cursorposition nach Snippet-Erstellung; $ 1 - erste Position, $ 2 - zweite usw., $ 0 - letzte Position (optional)
  • Beschreibung - Snippet-Beschreibung (optional)


Wir speichern die Datei. Wir geben log in das Skript ein, drücken die Tabulatortaste oder die Eingabetaste und erhalten console.log () mit dem Cursor zwischen den Klammern.



Erstellen wir ein Snippet für die for-of-Schleife:



"For-of loop": {
  "prefix": "fo",
  "body": [
    "for (const ${1:item} of ${2:arr}) {",
    "\t$0",
    "}"
  ]
},

      
      





  • Mehrzeilige Snippets werden mithilfe eines Arrays erstellt
  • $ {number: value}; $ {1: item} bedeutet erste Cursorposition mit Standardelementwert; Dieser Wert wird nach dem Erstellen eines Snippets sowie nach dem Verschieben an die nächste Position des Cursors zur schnellen Bearbeitung hervorgehoben
  • \ t - ein Einzug (die Größe der Lücke wird durch die entsprechenden Einstellungen des Editors oder in meinem Fall durch die Prettier- Erweiterung bestimmt ), \ t \ t - zwei Einrückungen usw.


Wir geben fo in das Skript ein, drücken die Tabulatortaste oder die Eingabetaste, wir erhalten:



for (const item of arr) {

}

      
      





mit hervorgehobenem Element. Drücken Sie die Tabulatortaste, arr wird hervorgehoben. Drücken Sie erneut die Tabulatortaste und gehen Sie zur zweiten Zeile.



Hier sind einige weitere Beispiele:



"For-in loop": {
  "prefix": "fi",
  "body": [
    "for (const ${1:key} in ${2:obj}) {",
    "\t$0",
    "}"
  ]
},
"Get one element": {
  "prefix": "qs",
  "body": "const $1 = ${2:document}.querySelector('$0')"
},
"Get all elements": {
  "prefix": "qsa",
  "body": "const $1 = [...${2:document}.querySelectorAll('$0')]"
},
"Add listener": {
  "prefix": "al",
  "body": [
    "${1:document}.addEventListener('${2:click}', (${3:{ target }}) => {",
    "\t$0",
    "})"
  ]
},
"Async function": {
  "prefix": "af",
  "body": [
    "const $1 = async ($2) => {",
    "\ttry {",
    "\t\tconst response = await fetch($3)",
    "\t\tconst data = await res.json()",
    "\t\t$0",
    "\t} catch (err) {",
    "\t\tconsole.error(err)",
    "\t}",
    "}"
  ]
}

      
      





HTML-Snippets folgen demselben Prinzip. So sieht die HTML-Vorlage aus:



{
  "HTML Template": {
    "prefix": "html",
    "body": [
      "<!DOCTYPE html>",
      "<html",
      "\tlang='en'",
      "\tdir='ltr'",
      "\titemscope",
      "\titemtype='https://schema.org/WebPage'",
      "\tprefix='og: http://ogp.me/ns#'",
      ">",
      "\t<head>",
      "\t\t<meta charset='UTF-8' />",
      "\t\t<meta name='viewport' content='width=device-width, initial-scale=1' />",
      "",
      "\t\t<title>$1</title>",
      "",
      "\t\t<meta name='referrer' content='origin' />",
      "\t\t<link rel='canonical' href='$0' />",
      "\t\t<link rel='icon' type='image/png' href='./icons/64x64.png' />",
      "\t\t<link rel='manifest' href='./manifest.json' />",
      "",
      "\t\t<!-- Security -->",
      "\t\t<meta http-equiv='X-Content-Type-Options' content='nosniff' />",
      "\t\t<meta http-equiv='X-XSS-Protection' content='1; mode=block' />",
      "",
      "\t\t<meta name='author' content='$3' />",
      "\t\t<meta name='description' content='$2' />",
      "\t\t<meta name='keywords' content='$4' />",
      "",
      "\t\t<meta itemprop='name' content='$1' />",
      "\t\t<meta itemprop='description' content='$2' />",
      "\t\t<meta itemprop='image' content='./icons/128x128.png' />",
      "",
      "\t\t<!-- Microsoft -->",
      "\t\t<meta http-equiv='x-ua-compatible' content='ie=edge' />",
      "\t\t<meta name='application-name' content='$1' />",
      "\t\t<meta name='msapplication-tooltip' content='$2' />",
      "\t\t<meta name='msapplication-starturl' content='/' />",
      "\t\t<meta name='msapplication-config' content='browserconfig.xml' />",
      "",
      "\t\t<!-- Facebook -->",
      "\t\t<meta property='og:type' content='website' />",
      "\t\t<meta property='og:url' content='$0' />",
      "\t\t<meta property='og:title' content='$1' />",
      "\t\t<meta property='og:image' content='./icons/256x256.png' />",
      "\t\t<meta property='og:site_name' content='$1' />",
      "\t\t<meta property='og:description' content='$2' />",
      "\t\t<meta property='og:locale' content='en_US' />",
      "",
      "\t\t<!-- Twitter -->",
      "\t\t<meta name='twitter:title' content='$1' />",
      "\t\t<meta name='twitter:description' content='$2' />",
      "\t\t<meta name='twitter:url' content='$0' />",
      "\t\t<meta name='twitter:image' content='./icons/128x128.png' />",
      "",
      "\t\t<!-- IOS -->",
      "\t\t<meta name='apple-mobile-web-app-title' content='$1' />",
      "\t\t<meta name='apple-mobile-web-app-capable' content='yes' />",
      "\t\t<meta name='apple-mobile-web-app-status-bar-style' content='#222' />",
      "\t\t<link rel='apple-touch-icon' href='./icons/256x256.png' />",
      "",
      "\t\t<!-- Android -->",
      "\t\t<meta name='theme-color' content='#eee' />",
      "\t\t<meta name='mobile-web-app-capable' content='yes' />",
      "",
      "\t\t<!-- Google Verification Tag -->",
      "",
      "\t\t<!-- Global site tag (gtag.js) - Google Analytics -->",
      "",
      "\t\t<!-- Global site tag (gtag.js) - Google Analytics -->",
      "",
      "\t\t<!-- Yandex Verification Tag -->",
      "",
      "\t\t<!-- Yandex.Metrika counter -->",
      "",
      "\t\t<!-- Mail Verification Tag -->",
      "",
      "\t\t<!-- JSON-LD -->",
      "\t\t<script type='application/ld+json'>",
      "\t\t\t{",
      "\t\t\t\t'@context': 'http://schema.org/',",
      "\t\t\t\t'@type': 'WebPage',",
      "\t\t\t\t'name': '$1',",
      "\t\t\t\t'image': [",
      "\t\t\t\t\t'$0icons/512x512.png'",
      "\t\t\t\t],",
      "\t\t\t\t'author': {",
      "\t\t\t\t\t'@type': 'Person',",
      "\t\t\t\t\t'name': '$3'",
      "\t\t\t\t},",
      "\t\t\t\t'datePublished': '2020-11-20',",
      "\t\t\t\t'description': '$2',",
      "\t\t\t\t'keywords': '$4'",
      "\t\t\t}",
      "\t\t</script>",
      "",
      "\t\t<!-- Google Fonts -->",
      "",
      "\t\t<style>",
      "\t\t\t/* Critical CSS */",
      "\t\t</style>",
      "",
      "\t\t<link rel='preload' href='./css/style.css' as='style'>",
      "\t\t<link rel='stylesheet' href='./css/style.css' />",
      "",
      "<link rel='preload' href='./script.js' as='script'>",
      "\t</head>",
      "\t<body>",
      "\t\t<!-- HTML5 -->",
      "\t\t<header>",
      "\t\t\t<h1>$1</h1>",
      "\t\t\t<nav>",
      "\t\t\t\t<a href='#' target='_blank' rel='noopener'>Link 1</a>",
      "\t\t\t\t<a href='#' target='_blank' rel='noopener'>Link 2</a>",
      "\t\t\t</nav>",
      "\t\t</header>",
      "",
      "\t\t<main></main>",
      "",
      "\t\t<footer>",
      "\t\t\t<p>© 2020. All rights reserved</p>",
      "\t\t</footer>",
      "",
      "\t\t<script src='./script.js' type='module'></script>",
      "\t</body>",
      "</html>"
    ],
    "description": "Create Modern HTML Template"
  }
}

      
      





Wir geben HTML ein, drücken die Tabulatortaste oder die Eingabetaste, wir erhalten das Markup. Die Cursorpositionen werden in der folgenden Reihenfolge definiert: Anwendungsname (Titel), Beschreibung (Beschreibung), Autor (Autor), Schlüsselwörter (Schlüsselwörter), Adresse (URL).



Erweiterung



Die VSCode-Site verfügt über eine hervorragende Dokumentation zum Erstellen von Erweiterungen .



Wir werden zwei Optionen für die Erweiterung erstellen: Snippet-Formular und CLI-Formular. Wir werden die zweite Option auf dem Visual Studio Marketplace veröffentlichen .



Beispiele für Erweiterungen in Form von Snippets:





CLI-Formularerweiterungen sind weniger beliebt, wahrscheinlich weil es "echte" CLIs gibt.



Erweiterung in Form von Snippets


Um Erweiterungen für VSCode zu entwickeln , benötigen wir zusätzlich zu Node.js und Git ein paar weitere Bibliotheken, genauer gesagt eine Bibliothek und ein Plugin, nämlich: yeoman und generator-code . Installieren Sie sie global:



npm i -g yo generator-code
// 
yarn global add yo generator-code

      
      





Wir führen den Befehl yo code aus, wählen New Code Snippets aus und beantworten Fragen.







Es bleibt, das zuvor erstellte HTML-Snippet in die Datei snippets / snippets.code-snippets zu kopieren (Snippet-Dateien können auch die Erweiterung json haben), die Datei package.json und README.md zu bearbeiten und die Erweiterung auf dem Marktplatz zu veröffentlichen. Wie Sie sehen können, ist alles sehr einfach. Zu einfach, dachte ich und beschloss, eine Erweiterung in Form einer CLI zu erstellen.



CLI-Erweiterung


Führen Sie den Befehl yo code erneut aus. Dieses Mal wählen wir Neue Erweiterung (TypeScript) (keine Angst, unser Code enthält fast kein TypeScript , und wo es sich befindet, werde ich die erforderlichen Erklärungen geben), beantworten Sie die Fragen.







Öffnen Sie das Projekt im Editor, um sicherzustellen, dass die Erweiterung funktioniert:



cd htmltemplate
code .

      
      





Drücken Sie links F5 oder die Schaltfläche Ausführen (Strg / Befehlstaste + Umschalt + D) und oben die Schaltfläche Debugging starten. Manchmal wird beim Start ein Fehler angezeigt. In diesem Fall brechen wir den Start ab (Abbrechen) und wiederholen den Vorgang.



Klicken Sie im sich öffnenden Editor auf Ansicht -> Befehlspalette (Strg / Befehlstaste + Umschalt + P), geben Sie Hallo ein und wählen Sie Hallo Welt.







Wir erhalten eine Informationsnachricht von VSCode und eine entsprechende Nachricht (Glückwünsche) in der Konsole.







Von allen Dateien im Projekt interessieren uns package.json und src / extension.ts. Das Verzeichnis src / test und die Datei vsc-extension-quickstart.md können entfernt werden.



Werfen wir einen Blick auf extension.ts (Kommentare aus Gründen der Lesbarkeit entfernt):



//   VSCode
import * as vscode from 'vscode'

// ,    
export function activate(context: vscode.ExtensionContext) {
  // ,    ,
  //     
  console.log('Congratulations, your extension "htmltemplate" is now active!')

  //  
  //  -   
  // htmltemplate -  
  // helloWorld -  
  let disposable = vscode.commands.registerCommand(
    'htmltemplate.helloWorld',
    () => {
      //  ,   
      //    
      vscode.window.showInformationMessage('Hello World from htmltemplate!')
    }
  )

  //  
  //   ,     "/",
  //     ""
  context.subscriptions.push(disposable)
}

// ,    
export function deactivate() {}

      
      





Wichtiger Punkt: 'extension.command' in extension.ts muss mit den Werten der Aktivierungsereignisse und Befehlsfelder in package.json übereinstimmen:



"activationEvents": [
  "onCommand:htmltemplate.helloWorld"
],
"contributes": {
  "commands": [
    {
      "command": "htmltemplate.helloWorld",
      "title": "Hello World"
    }
  ]
},

      
      





  • Befehle - Liste der Befehle
  • Aktivierungsereignisse - Funktionen, die während der Befehlsausführung aufgerufen werden sollen


Beginnen wir mit der Entwicklung der Erweiterung.



Wir möchten, dass unsere Erweiterung in der Funktionalität der Create-React -App oder der Vue-Cli ähnelt , d. H. Mit dem Befehl create wurde ein Projekt erstellt, das alle erforderlichen Dateien im Zielverzeichnis enthält.



Zuerst bearbeiten wir package.json:



"displayName": "HTML Template",
"activationEvents": [
  "onCommand:htmltemplate.create"
],
"contributes": {
  "commands": [
    {
      "command": "htmltemplate.create",
      "title": "Create Template"
    }
  ]
},

      
      





Erstellen Sie ein Verzeichnis src / components, um die Projektdateien zu speichern, die in das Zielverzeichnis kopiert werden.



Wir erstellen Projektdateien in Form von ES6-Modulen (VSCode verwendet standardmäßig ES6-Module (Export / Import), unterstützt jedoch CommonJS-Module (module.exports / require)): index.html.js, css / style.css.js , script.js usw. Der Inhalt der Dateien wird standardmäßig exportiert:



// index.html.js
export default `
<!DOCTYPE html>
<html
  lang="en"
  dir="ltr"
  itemscope
  itemtype="https://schema.org/WebPage"
  prefix="og: http://ogp.me/ns#"
>
  ...
</html>
`

      
      





Beachten Sie, dass bei diesem Ansatz alle Bilder (in unserem Fall Symbole) Base64-codiert sein müssen: Hier ist ein geeignetes Online-Tool . Das Vorhandensein der Zeile "data: image / png; base64" am Anfang der konvertierten Datei ist nicht von grundlegender Bedeutung.



Wir werden fs-extra verwenden , um Dateien zu kopieren (schreiben) . Die outputFile-Methode dieser Bibliothek funktioniert genauso wie die integrierte Node.js-Methode writeFile, erstellt jedoch auch ein Verzeichnis für die zu schreibende Datei, wenn sie nicht vorhanden ist. Wenn Sie beispielsweise create css / style.css angegeben haben, das css-Verzeichnis jedoch nicht vorhanden ist, wird es von outputFile erstellt und schreibt dort style.css (writeFile löst eine Ausnahme aus, wenn kein Verzeichnis vorhanden ist).



Die Datei extension.ts sieht folgendermaßen aus:



import * as vscode from 'vscode'
//   fs-extra
const fs = require('fs-extra')
const path = require('path')

//   , ,   
import indexHTML from './components/index.html.js'
import styleCSS from './components/css/style.css.js'
import scriptJS from './components/script.js'
import icon64 from './components/icons/icon64.js'
// ...

export function activate(context: vscode.ExtensionContext) {
  console.log('Congratulations, your extension "htmltemplate" is now active!')

  let disposable = vscode.commands.registerCommand(
    'htmltemplate.create',
    () => {
      //  ,       html-template
      // filename: string  TypeScript-,
      //   ,  ,
      //   
      const folder = (filename: string) =>
        path.join(vscode.workspace.rootPath, `html-template/${filename}`)

      //    
      // files: string[] ,    files   
      const files: string[] = [
        indexHTML,
        styleCSS,
        scriptJS,
        icon64,
        ...
      ]

      //    
      //  ,        
      const fileNames: string[] = [
        'index.html',
        'css/style.css',
        'script.js',
        'server.js',
        'icons/64x64.png',
        ...
      ]

      ;(async () => {
        try {
          //    
          for (let i = 0; i < files.length; i++) {

            //  outputFile       :
            //    ( ),     (  UTF-8)

            //     png,
            // ,     Base64-:
            //   
            if (fileNames[i].includes('png')) {
              await fs.outputFile(folder(fileNames[i]), files[i], 'base64')
            // ,    
            } else {
              await fs.outputFile(folder(fileNames[i]), files[i])
            }
          }

          //     
          return vscode.window.showInformationMessage(
            'All files created successfully'
          )
        } catch {
          //   
          return vscode.window.showErrorMessage('Failed to create files')
        }
      })()
    }
  )

  context.subscriptions.push(disposable)
}

export function deactivate() {}

      
      





Erstellen Sie src / global.d.ts mit folgendem Inhalt, um zu verhindern, dass TypeScript auf das Fehlen importierter Moduldateien achtet:



declare module '*'

      
      





Testen wir die Erweiterung. Öffnen Sie es im Editor:



cd htmltemplate
code .

      
      





Starten Sie das Debuggen (F5). Wechseln Sie in das Zielverzeichnis (z. B. Testverzeichnis) und führen Sie den Befehl create in der Befehlspalette aus.







Wir erhalten eine Informationsnachricht über die erfolgreiche Erstellung von Dateien. Hurra!







Veröffentlichen einer Erweiterung für den Visual Studio Marketplace


Um Erweiterungen für VSCode veröffentlichen zu können, müssen Sie folgende Schritte ausführen:



  • Erstellen Sie ein Konto auf dem Marktplatz (merken Sie sich den Wert des Publisher-Felds).
  • Installieren Sie die vsce- Bibliothek global


Package package.json:



{
  "name": "htmltemplate",
  "displayName": "HTML Template",
  "description": "Modern HTML Starter Template",
  "version": "1.0.0",
  "publisher": "puslisher-name",
  "license": "MIT",
  "keywords": [
    "html",
    "html5",
    "css",
    "css3",
    "javascript",
    "js"
  ],
  "icon": "build/128x128.png",
  "author": {
    "name": "Author Name @githubusername"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/username/dirname"
  },
  "engines": {
    "vscode": "^1.51.0"
  },
  "categories": [
    "Snippets"
  ],
  "activationEvents": [
    "onCommand:htmltemplate.create"
  ],
  "main": "./dist/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "htmltemplate.create",
        "title": "Create Template"
      }
    ]
  },
  ...
}

      
      





README.md bearbeiten.



Führen Sie den Befehl vsce package im Erweiterungsverzeichnis aus, um ein veröffentlichtes Paket mit der Erweiterung vsix zu erstellen. Wir erhalten die Datei htmltemplate-1.0.0.vsix.



Klicken Sie auf der Seite zum Verwalten von Marktplatzerweiterungen auf die Schaltfläche Neue Erweiterung und wählen Sie Visual Studio Code aus. Übertragen oder laden Sie die VSIX-Datei in das modale Fenster. Wir warten auf den Abschluss der Überprüfung.







Nachdem neben der Versionsnummer ein grünes Häkchen angezeigt wird, kann die Erweiterung in VSCode installiert werden.







Um die Erweiterung zu aktualisieren, müssen Sie die Versionsnummer in package.json ändern, eine VSIX-Datei generieren und auf den Marktplatz hochladen, indem Sie auf die Schaltfläche Weitere Aktionen klicken und Aktualisieren auswählen.



Wie Sie sehen, ist das Erstellen und Veröffentlichen von Erweiterungen für VSCode nichts Übernatürliches. Lassen Sie mich diesbezüglich Abschied nehmen.



Im nächsten Teil werden wir eine vollständige Befehlszeilenschnittstelle erstellen, zuerst mit dem Heroku-Framework - oclif , dann ohne. Unsere Node.js-CLI unterscheidet sich stark von einer Erweiterung. Sie verfügt über eine gewisse Visualisierung und die Möglichkeit, git optional zu initialisieren und Abhängigkeiten zu installieren.



Ich hoffe, Sie haben etwas Interessantes für sich gefunden. Vielen Dank für Ihre Aufmerksamkeit.



All Articles