[Разработка веб-сайтов, JavaScript, Программирование] Сниппет, расширение для VSCode и CLI. Часть 1
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Доброго времени суток, друзья!
В процессе разработки Современного стартового HTML-шаблона я задумался о расширении возможностей его использования. На тот момент варианты его применения ограничивались клонированием репозитория и скачиванием архива. Так появились HTML-сниппет и расширение для Microsoft Visual Studio Code — HTML Template, а также интерфейс командной строки — create-modern-template. Конечно, указанные инструменты далеки от совершенства и я буду их дорабатывать по мере сил и возможностей. Однако, в процессе их создания я узнал несколько интересных вещей, которыми и хочу с вами поделиться.
В этой части мы рассмотрим сниппет и расширение, а CLI — в следующей.
Если вас интересует лишь исходный код, вот ссылка на репозиторий.
Сниппет (Snippet)
Что такое сниппет? Если коротко, сниппет — это заготовка, которую использует редактор для автозаполнения (автодополнения кода).
В VSCode встроен Emmet (официальный сайт, Emmet in Visual Studio Code), который использует многочисленные HTML, CSS и JS-сниппеты для помощи в написании кода. Набираем в редакторе (в .html) !, нажимаем Tab или Enter, получаем готовую html5-разметку. Набираем nav>ul>li*3>a.link>img, нажимаем Tab, получаем:
<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>
и т.д.
Кроме встроенных, VSCode предусматривает возможность использования пользовательских сниппетов. Для их создания необходимо перейти в File -> Preferences -> User Snippets (или нажать на кнопку Manage в левом нижнем углу и выбрать User Snippets). Настройки для каждого языка хранятся в соответствующем JSON-файле (для HTML в html.json, для JavaScript в javascript.json и т.д.).
Потренируемся создавать JS-сниппеты. Находим файл javascript.json и открываем его.
Видим комментарии, кратко описывающие правила создания сниппетов. Более подробную информацию о создании пользовательских сниппетов в VSCode можно найти здесь.
Начнем с чего-нибудь простого. Создадим сниппет для console.log(). Вот как он выглядит:
"Print to console": {
"prefix": "log",
"body": "console.log($0)",
"description": "Create console.log()"
},
- Print to console — ключ объекта, название сниппета (обязательно)
- prefix — сокращение для сниппета (обязательно)
- body — сам сниппет (обязательно)
- $число — положение курсора после создания сниппета; $1 — первое положение, $2 — второе и т.д., $0 — последнее положение (опционально)
- description — описание сниппета (опционально)
Сохраняем файл. Набираем log в скрипте, нажимаем Tab или Enter, получаем console.log() с курсором между скобками.
Создадим сниппет для цикла for-of:
"For-of loop": {
"prefix": "fo",
"body": [
"for (const ${1:item} of ${2:arr}) {",
"\t$0",
"}"
]
},
- Сниппеты, состоящие из нескольких строк, создаются с помощью массива
- ${число: значение}; ${1:item} означает первое положение курсора со значением item по умолчанию; данное значение выделяется после создания сниппета, а также после перехода к следующему положению курсора для быстрого редактирования
- \t — один отступ (величина оступа определяется соответствующими настройками редактора или, как в моем случае, расширения Prettier), \t\t — два отступа и т.д.
Набираем в скрипте fo, нажимаем Tab или Enter, получаем:
for (const item of arr) {
}
с выделенным item. Нажимаем Tab, выделяется arr. Еще раз нажимаем Tab, переходим на вторую строку.
Вот еще несколько примеров:
"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-сниппеты строятся по такому же принципу. Вот как выглядит HTML Template:
{
"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"
}
}
Набираем html, нажимаем Tab или Enter, получаем разметку. Положения курсора определены в следующем порядке: название приложения (title), описание (description), автор (author), ключевые слова (keywords), адрес (url).
Расширение (Extension)
На сайте VSCode имеется отличная документация по созданию расширений.
Мы создадим два варианта расширения: в форме сниппетов и в форме CLI. Второй вариант опубликуем в Visual Studio Marketplace.
Примеры расширений в форме сниппетов:
Расширения в форме CLI менее популярны, вероятно, по той причине, что существуют «настоящие» CLI.
Расширение в форме сниппетов
Для разработки расширений для VSCode, нам, кроме Node.js и Git, потребуется еще парочка библиотек, точнее, одна библиотека и плагин, а именно: yeoman и generator-code. Устанавливаем их глобально:
npm i -g yo generator-code
// или
yarn global add yo generator-code
Выполняем команду yo code, выбираем New Code Snippets, отвечаем на вопросы.
Осталось скопировать созданный нами ранее HTML-сниппет в файл snippets/snippets.code-snippets (файлы сниппетов также могут иметь расширение json), отредактировать package.json и README.md, и можно публиковать расширение в маркетплейсе. Как видите, все очень просто. Слишком просто, подумал я, и решил создать расширение в форме CLI.
Расширение в форме CLI
Снова выполняем команду yo code. На этот раз выбираем New Extension (TypeScript) (не бойтесь, TypeScript в нашем коде почти не будет, а там, где будет, я дам необходимые разъяснения), отвечаем на вопросы.
Для того, чтобы убедиться в работоспособности расширения, открываем проект в редакторе:
cd htmltemplate
code .
Нажимаем F5 или на кнопку Run (Ctrl/Cmd+Shift+D) слева и кнопку Start Debugging сверху. Иногда при запуске можно получить ошибку. В этом случае отменяем запуск (Cancel) и повторяем процедуру.
В открывшемся редакторе нажимаем View -> Command Palette (Ctrl/Cmd+Shift+P), набираем hello и выбираем Hello World.
Получаем информационное сообщение от VSCode и соответствующее сообщение (поздравление) в консоли.
Из всех файлов, имеющихся в проекте, нас интересуют package.json и src/extension.ts. Директорию src/test и файл vsc-extension-quickstart.md можно удалить.
Заглянем в extension.ts (комментарии удалены для удобочитаемости):
// импорт функционала 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() {}
Важный момент: 'расширение.команда' в extension.ts должно совпадать со значениями полей activationEvents и command в package.json:
"activationEvents": [
"onCommand:htmltemplate.helloWorld"
],
"contributes": {
"commands": [
{
"command": "htmltemplate.helloWorld",
"title": "Hello World"
}
]
},
- commands — список команд
- activationEvents — функции, вызываемые при выполнении команд
Приступаем к разработке расширения.
Мы хотим, чтобы наше расширение по функционалу напоминало create-react-app или vue-cli, т.е. по команде create создавало проект, содержащий все необходимые файлы, в целевой директории.
Для начала отредактируем package.json:
"displayName": "HTML Template",
"activationEvents": [
"onCommand:htmltemplate.create"
],
"contributes": {
"commands": [
{
"command": "htmltemplate.create",
"title": "Create Template"
}
]
},
Создаем директорию src/components для хранения файлов проекта, которые будут копироваться в целевую директорию.
Создаем файлы проекта в виде ES6-модулей (VSCode по умолчанию использует ES6-модули (export/import), но поддерживает и CommonJS-модули (module.exports/require)): index.html.js, css/style.css.js, script.js и т.д. Содержимое файлов экспортируется по умолчанию:
// 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>
`
Обратите внимание, что при таком подходе все изображения (в нашем случае, иконки) должны быть закодированы в Base64: вот один из подходящих онлайн-инструментов. Наличие строки «data:image/png;base64,» в начале преобразованного файла принципиального значение не имеет.
Для копирования (записи) файлов мы будем использовать fs-extra. Метод outputFile данной библиотеки делает томе самое, что и встроенный Node.js-метод writeFile, но также создает директорию для записываемого файла при ее отсутствии: например, если мы указали создать css/style.css, а директории css не существует, outputFile создаст ее и запишет туда style.css (writeFile при отсутствии директории выбросит исключение).
Файл extension.ts выглядит следующим образом:
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() {}
Для того, чтобы TypeScript не обращал внимания на отсутствие типов импортируемых файлов-модулей, создадим src/global.d.ts следующего содержания:
declare module '*'
Протестируем расширение. Открываем его в редакторе:
cd htmltemplate
code .
Запускаем отладку (F5). Переходим в целевую директорию (test-dir, например) и выполняем команду create в Command Palette.
Получаем информационное сообщение об успешном создании файлов. Ура!
Публикация расширения в Visual Studio Marketplace
Для того, чтобы иметь возможность публиковать расширения для VSCode, необходимо сделать следующее:
- Создать аккаунт в маркетплейсе (запомните значение поля publisher)
- Глобально установить библиотеку vsce
Редактируем 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.
Выполняем команду vsce package в директории расширения для создания публикуемого пакета с расширением vsix. Получаем файл htmltemplate-1.0.0.vsix.
На странице управления расширениями маркетплейса нажимаем кнопку New extension и выбираем Visual Studio Code. Переносим или загружаем в модальное окно VSIX-файл. Ждем завершения проверки.
После того, как рядом с номером версии появилась зеленая галочка, расширение становится доступным для установки в VSCode.
Для обновления расширения необходимо изменить номер версии в package.json, сгенерировать VSIX-файл и загрузить его в маркетплейс, нажав на кнопку More actions и выбрав Update.
Как видите, в создании и публикации расширений для VSCode нет ничего сверхестественного. На этом позвольте откланяться.
В следующей части мы создадим полноценный интерфейс командной строки сначала с помощью фреймворка от Heroku — oclif, затем без него. Наш Node.js-CLI будет сильно отличаться от расширения, в нем будет присутствовать некоторая визуализация, возможность опциональной инициализации git и установки зависимостей.
Надеюсь, вы нашли для себя что-нибудь интересное. Благодарю за внимание.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Разработка игр, Unity] Представляем Owlcat Mono Profiler для Unity
- [Программирование, .NET, C#] Вышел .NET 5. И что?
- [Разработка веб-сайтов, JavaScript, Accessibility] Безжалостное избавление от Layout Shift на netlify.com (перевод)
- [Промышленное программирование, Программирование микроконтроллеров, Разработка под Arduino, Производство и разработка электроники] Кому в микроконтроллере жить хорошо?
- [Разработка веб-сайтов, Программирование, TypeScript] ТайпСкрип: Ох уж эта весёлая система типов
- [Разработка веб-сайтов, JavaScript] JavaScript исполняется 25 лет: краткая история языка и скидка 50% на WebStorm
- [Программирование, Разработка под iOS, Objective C, Swift] Модуляризация iOS-приложения: зачем и как мы разбиваем Badoo на модули
- [Программирование, C++] Небольшие, но важные функции (перевод)
- [Программирование, Управление разработкой, Управление персоналом, Карьера в IT-индустрии] О плюсах парного программирования
- [Программирование, CRM-системы] Интегрируем web-телефон в свою систему
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_javascript, #_programmirovanie (Программирование), #_javascript, #_snippet, #_extension, #_visual_studio_code, #_vscode, #_command_line, #_cli, #_snippet (сниппет), #_rasshirenie (расширение), #_komandnaja_stroka (командная строка), #_interfejs_komandnoj_stroki (интерфейс командной строки), #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_javascript, #_programmirovanie (
Программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 01:57
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Доброго времени суток, друзья! В процессе разработки Современного стартового HTML-шаблона я задумался о расширении возможностей его использования. На тот момент варианты его применения ограничивались клонированием репозитория и скачиванием архива. Так появились HTML-сниппет и расширение для Microsoft Visual Studio Code — HTML Template, а также интерфейс командной строки — create-modern-template. Конечно, указанные инструменты далеки от совершенства и я буду их дорабатывать по мере сил и возможностей. Однако, в процессе их создания я узнал несколько интересных вещей, которыми и хочу с вами поделиться. В этой части мы рассмотрим сниппет и расширение, а CLI — в следующей. Если вас интересует лишь исходный код, вот ссылка на репозиторий. Сниппет (Snippet) Что такое сниппет? Если коротко, сниппет — это заготовка, которую использует редактор для автозаполнения (автодополнения кода). В VSCode встроен Emmet (официальный сайт, Emmet in Visual Studio Code), который использует многочисленные HTML, CSS и JS-сниппеты для помощи в написании кода. Набираем в редакторе (в .html) !, нажимаем Tab или Enter, получаем готовую html5-разметку. Набираем nav>ul>li*3>a.link>img, нажимаем Tab, получаем: <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> и т.д. Кроме встроенных, VSCode предусматривает возможность использования пользовательских сниппетов. Для их создания необходимо перейти в File -> Preferences -> User Snippets (или нажать на кнопку Manage в левом нижнем углу и выбрать User Snippets). Настройки для каждого языка хранятся в соответствующем JSON-файле (для HTML в html.json, для JavaScript в javascript.json и т.д.). Потренируемся создавать JS-сниппеты. Находим файл javascript.json и открываем его. Видим комментарии, кратко описывающие правила создания сниппетов. Более подробную информацию о создании пользовательских сниппетов в VSCode можно найти здесь. Начнем с чего-нибудь простого. Создадим сниппет для console.log(). Вот как он выглядит: "Print to console": {
"prefix": "log", "body": "console.log($0)", "description": "Create console.log()" },
Сохраняем файл. Набираем log в скрипте, нажимаем Tab или Enter, получаем console.log() с курсором между скобками. Создадим сниппет для цикла for-of: "For-of loop": {
"prefix": "fo", "body": [ "for (const ${1:item} of ${2:arr}) {", "\t$0", "}" ] },
Набираем в скрипте fo, нажимаем Tab или Enter, получаем: for (const item of arr) {
} с выделенным item. Нажимаем Tab, выделяется arr. Еще раз нажимаем Tab, переходим на вторую строку. Вот еще несколько примеров: "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-сниппеты строятся по такому же принципу. Вот как выглядит HTML Template: {
"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" } } Набираем html, нажимаем Tab или Enter, получаем разметку. Положения курсора определены в следующем порядке: название приложения (title), описание (description), автор (author), ключевые слова (keywords), адрес (url). Расширение (Extension) На сайте VSCode имеется отличная документация по созданию расширений. Мы создадим два варианта расширения: в форме сниппетов и в форме CLI. Второй вариант опубликуем в Visual Studio Marketplace. Примеры расширений в форме сниппетов: Расширения в форме CLI менее популярны, вероятно, по той причине, что существуют «настоящие» CLI. Расширение в форме сниппетов Для разработки расширений для VSCode, нам, кроме Node.js и Git, потребуется еще парочка библиотек, точнее, одна библиотека и плагин, а именно: yeoman и generator-code. Устанавливаем их глобально: npm i -g yo generator-code
// или yarn global add yo generator-code Выполняем команду yo code, выбираем New Code Snippets, отвечаем на вопросы. Осталось скопировать созданный нами ранее HTML-сниппет в файл snippets/snippets.code-snippets (файлы сниппетов также могут иметь расширение json), отредактировать package.json и README.md, и можно публиковать расширение в маркетплейсе. Как видите, все очень просто. Слишком просто, подумал я, и решил создать расширение в форме CLI. Расширение в форме CLI Снова выполняем команду yo code. На этот раз выбираем New Extension (TypeScript) (не бойтесь, TypeScript в нашем коде почти не будет, а там, где будет, я дам необходимые разъяснения), отвечаем на вопросы. Для того, чтобы убедиться в работоспособности расширения, открываем проект в редакторе: cd htmltemplate
code . Нажимаем F5 или на кнопку Run (Ctrl/Cmd+Shift+D) слева и кнопку Start Debugging сверху. Иногда при запуске можно получить ошибку. В этом случае отменяем запуск (Cancel) и повторяем процедуру. В открывшемся редакторе нажимаем View -> Command Palette (Ctrl/Cmd+Shift+P), набираем hello и выбираем Hello World. Получаем информационное сообщение от VSCode и соответствующее сообщение (поздравление) в консоли. Из всех файлов, имеющихся в проекте, нас интересуют package.json и src/extension.ts. Директорию src/test и файл vsc-extension-quickstart.md можно удалить. Заглянем в extension.ts (комментарии удалены для удобочитаемости): // импорт функционала 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() {} Важный момент: 'расширение.команда' в extension.ts должно совпадать со значениями полей activationEvents и command в package.json: "activationEvents": [
"onCommand:htmltemplate.helloWorld" ], "contributes": { "commands": [ { "command": "htmltemplate.helloWorld", "title": "Hello World" } ] },
Приступаем к разработке расширения. Мы хотим, чтобы наше расширение по функционалу напоминало create-react-app или vue-cli, т.е. по команде create создавало проект, содержащий все необходимые файлы, в целевой директории. Для начала отредактируем package.json: "displayName": "HTML Template",
"activationEvents": [ "onCommand:htmltemplate.create" ], "contributes": { "commands": [ { "command": "htmltemplate.create", "title": "Create Template" } ] }, Создаем директорию src/components для хранения файлов проекта, которые будут копироваться в целевую директорию. Создаем файлы проекта в виде ES6-модулей (VSCode по умолчанию использует ES6-модули (export/import), но поддерживает и CommonJS-модули (module.exports/require)): index.html.js, css/style.css.js, script.js и т.д. Содержимое файлов экспортируется по умолчанию: // 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> ` Обратите внимание, что при таком подходе все изображения (в нашем случае, иконки) должны быть закодированы в Base64: вот один из подходящих онлайн-инструментов. Наличие строки «data:image/png;base64,» в начале преобразованного файла принципиального значение не имеет. Для копирования (записи) файлов мы будем использовать fs-extra. Метод outputFile данной библиотеки делает томе самое, что и встроенный Node.js-метод writeFile, но также создает директорию для записываемого файла при ее отсутствии: например, если мы указали создать css/style.css, а директории css не существует, outputFile создаст ее и запишет туда style.css (writeFile при отсутствии директории выбросит исключение). Файл extension.ts выглядит следующим образом: 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() {} Для того, чтобы TypeScript не обращал внимания на отсутствие типов импортируемых файлов-модулей, создадим src/global.d.ts следующего содержания: declare module '*'
Протестируем расширение. Открываем его в редакторе: cd htmltemplate
code . Запускаем отладку (F5). Переходим в целевую директорию (test-dir, например) и выполняем команду create в Command Palette. Получаем информационное сообщение об успешном создании файлов. Ура! Публикация расширения в Visual Studio Marketplace Для того, чтобы иметь возможность публиковать расширения для VSCode, необходимо сделать следующее:
Редактируем 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. Выполняем команду vsce package в директории расширения для создания публикуемого пакета с расширением vsix. Получаем файл htmltemplate-1.0.0.vsix. На странице управления расширениями маркетплейса нажимаем кнопку New extension и выбираем Visual Studio Code. Переносим или загружаем в модальное окно VSIX-файл. Ждем завершения проверки. После того, как рядом с номером версии появилась зеленая галочка, расширение становится доступным для установки в VSCode. Для обновления расширения необходимо изменить номер версии в package.json, сгенерировать VSIX-файл и загрузить его в маркетплейс, нажав на кнопку More actions и выбрав Update. Как видите, в создании и публикации расширений для VSCode нет ничего сверхестественного. На этом позвольте откланяться. В следующей части мы создадим полноценный интерфейс командной строки сначала с помощью фреймворка от Heroku — oclif, затем без него. Наш Node.js-CLI будет сильно отличаться от расширения, в нем будет присутствовать некоторая визуализация, возможность опциональной инициализации git и установки зависимостей. Надеюсь, вы нашли для себя что-нибудь интересное. Благодарю за внимание. =========== Источник: habr.com =========== Похожие новости:
Разработка веб-сайтов ), #_javascript, #_programmirovanie ( Программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 01:57
Часовой пояс: UTC + 5