يوم جيد ، أيها الأصدقاء!
أثناء تطوير قالب كاتب HTML الحديث ، فكرت في توسيع إمكانية استخدامه. في ذلك الوقت ، اقتصرت خيارات استخدامه على استنساخ المستودع وتنزيل الأرشيف. هذه هي الطريقة التي ظهر بها مقتطف HTML وامتداده لـ Microsoft Visual Studio Code - قالب HTML ، بالإضافة إلى واجهة سطر الأوامر - إنشاء قالب حديث . بالطبع ، هذه الأدوات بعيدة كل البعد عن الكمال وسأقوم بتنقيحها بقدر ما أستطيع. ومع ذلك ، أثناء عملية إنشائها ، تعلمت بعض الأشياء المثيرة للاهتمام التي أريد مشاركتها معك.
في هذا الجزء سنلقي نظرة على المقتطف والامتداد ، و CLI في الجزء التالي.
إذا كنت مهتمًا فقط بالشفرة المصدرية ، فإليك رابط المستودع .
مقتطف
ما هو المقتطف؟ باختصار ، المقتطف عبارة عن قالب يستخدمه المحرر للإكمال التلقائي (إكمال التعليمات البرمجية).
يحتوي 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 القدرة على استخدام المقتطفات المخصصة. لإنشائها ، انتقل إلى ملف -> تفضيلات -> مقتطفات المستخدم (أو انقر فوق الزر إدارة في الزاوية اليسرى السفلية وحدد قصاصات المستخدم). يتم تخزين إعدادات كل لغة في ملف 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()"
},
- طباعة إلى وحدة التحكم - مفتاح الكائن ، اسم المقتطف (مطلوب)
- بادئة - اختصار للمقتطف (مطلوب)
- الجسم - المقتطف نفسه (مطلوب)
- number $ - موضع المؤشر بعد إنشاء المقتطف ؛ 1 دولار - المركز الأول ، 2 دولار - الثانية ، إلخ ، 0 دولار - المركز الأخير (اختياري)
- الوصف - وصف المقتطف (اختياري)
نحن نحفظ الملف. نكتب log في البرنامج النصي ، نضغط على Tab أو Enter ، نحصل على console.log () مع المؤشر بين قوسين.
دعنا ننشئ مقتطفًا للحلقة من أجل:
"For-of loop": {
"prefix": "fo",
"body": [
"for (const ${1:item} of ${2:arr}) {",
"\t$0",
"}"
]
},
- يتم إنشاء مقتطفات متعددة الخطوط باستخدام مصفوفة
- $ {number: value}؛ $ {1: item} يعني موضع المؤشر الأول بقيمة العنصر الافتراضية ؛ يتم تمييز هذه القيمة بعد إنشاء قصاصة ، وكذلك بعد الانتقال إلى الموضع التالي للمؤشر للتحرير السريع
- \ t - مسافة بادئة واحدة (يتم تحديد مقدار المسافة بواسطة إعدادات المحرر المقابلة أو ، في حالتي ، الامتداد الأجمل ) ، \ t \ t - مسافة بادئة ، إلخ.
نكتب fo في البرنامج النصي ، نضغط على Tab أو Enter ، نحصل على:
for (const item of arr) {
}
مع تمييز العنصر. اضغط على 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:
{
"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 ، نحصل على الترميز. يتم تحديد مواضع المؤشر بالترتيب التالي: اسم التطبيق (العنوان) ، الوصف (الوصف) ، المؤلف (المؤلف) ، الكلمات الرئيسية (الكلمات الرئيسية) ، العنوان (عنوان url).
تمديد
يحتوي موقع VSCode على وثائق ممتازة حول ملحقات المبنى .
سننشئ خيارين للامتداد: نموذج المقتطف ونموذج CLI. سننشر الخيار الثاني في Visual Studio Marketplace .
أمثلة على الامتدادات في شكل مقتطفات:
تعد امتدادات نماذج CLI أقل شيوعًا ، ربما بسبب وجود CLIs "حقيقية".
التمديد في شكل قصاصات
لتطوير امتدادات لـ 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 مرة أخرى. هذه المرة نختار ملحق جديد (TypeScript) (لا تخف ، لن يكون هناك TypeScript تقريبًا في الكود الخاص بنا ، وحيثما هو ، سأقدم التفسيرات اللازمة) ، أجب على الأسئلة.
للتأكد من أن الامتداد يعمل ، افتح المشروع في المحرر:
cd htmltemplate code .
اضغط على F5 أو زر التشغيل (Ctrl / Cmd + Shift + D) على اليسار وزر بدء التصحيح في الأعلى. في بعض الأحيان تحصل على خطأ عند بدء التشغيل. في هذه الحالة ، قم بإلغاء التشغيل (إلغاء) وكرر الإجراء.
في المحرر الذي يفتح ، انقر فوق عرض -> لوحة الأوامر (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.command' في extension.ts مع قيم حقول التنشيط والأوامر في package.json:
"activationEvents": [
"onCommand:htmltemplate.helloWorld"
],
"contributes": {
"commands": [
{
"command": "htmltemplate.helloWorld",
"title": "Hello World"
}
]
},
- الأوامر - قائمة الأوامر
- activationEvents - الوظائف التي سيتم استدعاؤها أثناء تنفيذ الأمر
لنبدأ في تطوير الامتداد.
نريد أن يكون امتدادنا مشابهًا لوظيفة إنشاء التطبيق أو تطبيق 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 بشكل افتراضي (تصدير / استيراد) ، لكنه يدعم وحدات CommonJS (module.exports / required)): 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 المضمنة ، ولكنها تنشئ أيضًا دليلًا للملف الذي تتم كتابته إذا لم يكن موجودًا: على سبيل المثال ، إذا حددنا إنشاء css / style.css ، ولم يكن دليل css موجودًا ، فسيقوم ملف الإخراج بإنشائه وسوف يكتب 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 في لوحة الأوامر.
نتلقى رسالة إعلامية حول الإنشاء الناجح للملفات. الصيحة!
نشر امتداد إلى Visual Studio Marketplace
لكي تتمكن من نشر الامتدادات لـ VSCode ، عليك القيام بما يلي:
- إنشاء حساب في السوق (تذكر قيمة حقل الناشر)
- قم بتثبيت مكتبة vsce عالميًا
تحرير الحزمة. 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.
في صفحة إدارة ملحقات السوق ، انقر فوق الزر امتداد جديد وحدد Visual Studio Code. قم بنقل ملف VSIX أو تحميله في النافذة المشروطة. نحن في انتظار اكتمال التحقق.
بعد ظهور علامة اختيار خضراء بجوار رقم الإصدار ، يصبح الامتداد متاحًا للتثبيت في VSCode.
لتحديث الامتداد ، تحتاج إلى تغيير رقم الإصدار في package.json ، وإنشاء ملف VSIX وتحميله إلى السوق من خلال النقر على زر المزيد من الإجراءات وتحديد تحديث.
كما ترى ، لا يوجد شيء خارق للطبيعة في إنشاء ونشر امتدادات لـ VSCode. على هذا ، دعني آخذ إجازتي.
في الجزء التالي ، سننشئ واجهة سطر أوامر كاملة ، أولاً باستخدام إطار عمل oclif الخاص بـ Heroku ، ثم بدونها. سيكون Node.js-CLI الخاص بنا مختلفًا تمامًا عن الامتداد ، وسيكون له بعض التصور ، والقدرة على تهيئة تبعيات git وتثبيت اختياريًا.
أتمنى أن تجد شيئًا ممتعًا لنفسك. شكرآ لك على أهتمامك.