VSCode⼩程序扩展开发
写在前⾯
为什么要开发这个扩展呢,是因为开发者⼯具⾃⾝不⽀持页⾯引⼊组件的跳转,⼈⼯根据引⼊组件路径查看对应代码的⽅式,效率偏低。就形如这样的json⽂件,引⼊了多个组
件,⽐如要查看
"/component/card-bg/card-bg"组件的源代码,通过⼈⼯检索⽬录查对应⽂件,显然不如ctrl+⿏标左键单击⼀下这个组件路径,跳转到对应的组件⽂档,更⾼效。作为⼀个有追求
的程序员。我们不能只做⼯具的使⽤者,我们要升级为⼯具的开发者。
先看看效果,功能展⽰如下:按下ctrl+⿏标左键,就能实现下⾯5种跳转功能。
1. app.json中的分包页⾯跳转
2. js⽂件中的wx.Launch跳转
3. wxml⽂件中标签bindTap属性绑定的⽅法跳转
4. wxml⽂件中wxs标签src路径跳转
5. js⽂件中的ajax url跳转
这个插件不仅可以在VSCode编辑器中使⽤,也可以在开发者⼯具中使⽤,效果⼀模⼀样。在开发者⼯具中的使⽤⽅法如下:
1. 开发者⼯具⼯具栏--编辑-->打开编辑器扩展⽬录-将VSCode的扩展复制进去,假如你已经安装了某个扩展,这个扩展⼀般在C:\Users\xxx⽤户名\.vscode\extensions路径
下。
2. 开发者⼯具⼯具栏--编辑-->管理编辑器扩展, 将VSCode扩展ID添加到扩展列表。扩展ID的查⽅式见下图。
3. 重启开发者⼯具。
如何开发出这样的VSCode ⼩程序扩展?
step1 安装vscode扩展开发脚⼿架yo,generator-code和vsce
npm install -g cnpm --registry=registry.
cnpm install -g yo generator-code vsce
step2 ⽤yo初始化扩展项⽬
yo code
==========================================================================
We're constantly looking for ways to make yo better!
May we anonymously report usage statistics to improve the tool over time?
More info: github/yeoman/insight & yeoman.io
========================================================================== No
_-----_    ╭──────────────────────────╮
|      |    │  Welcome to the Visual  │
|--(o)--|    │  Studio Code Extension  │
`---------´  │        generator!        │
( _´U`_ )    ╰──────────────────────────╯
/___A___\  /
|  ~  |
__'.___.'__
´  `  |° ´ Y `
What type of extension do you want to create? New Extension (JavaScript)
What's the name of your extension? wx-minipro-helper
What's the identifier of your extension? wx-minipro-helper
What's the description of your extension? ⼩程序开发助⼿,⽬前只实现了定义跳转功能
Enable JavaScript type checking in'jsconfig.json'? Yes
Initialize a git repository Yes
Which package manager to use yarn
step3  修改package.json⽂件
{
"name": "wx-minipro-helper",
"displayName": "wx-minipro-helper",
"description": "第⼀版本只实现了开发者⼯具不⽀持的 1.app.json内的页⾯路径跳转; 2. 页⾯下的json⽂件中引⼊的组件路径跳转; 3.页⾯⽂件下的js⽂件中的引⼊外部⼯具库跳转; 4.wx.Launch跳转; 5.wxml标签中    "version": "0.0.3",
"keywords": [ // 添加搜索关键字
"wx",
"minipro",
"helper"
],
"publisher": "wph", // 添加发布者
"icon": "src/images/icon.png", // 添加图标⾄少是128*128
"main": "src/extension", // 修改⼊⼝⽂件
"engines": {
"vscode": "^1.5.0" // 依赖的vscode版本
},
"categories": [
"Other"
]
,
"activationEvents": [ //启动时触发的命令
"*"
],
"contributes": {
"commands": [
{
"command": "extension.wx-minipro-helper",
"title": "wx-minipro-helper"
}
]
},
"scripts": {
"lint": "eslint .",
"pretest": "yarn run lint",
"test": "node ./test/runTest.js"
},
"devDependencies": {
"@types/glob": "^7.1.3",
"@types/mocha": "^8.0.0",
"@types/node": "^12.11.7",
"@types/vscode": "^1.5.0",
"eslint": "^7.9.0",
"fs": "^0.0.1-security",
"glob": "^7.1.6",
"mocha": "^8.1.3",
"os": "^0.1.1",
"path": "^0.12.7",
"typescript": "^4.0.2",
"vscode": "^1.1.37",
"vscode-test": "^1.4.0"
},
"bugs": {
"url": "github/wph/wx-minipro-helper/issues"
},
"repository": {
"type": "gitee",
"url": "gitee/getbetter/wx-minipro-helper.git"
},
"homepage": "gitee/getbetter/wx-minipro-helper/blob/master/README.md"
}
step4  写扩展的功能,扩展⼊⼝extension.js内容如下
const vscode = require('vscode');
/**
* 插件被激活时触发,所有代码总⼊⼝
* @param {*} context 插件上下⽂
*/
exports.activate = function (context) {
console.log('扩展“wx-minipro-helper”已被激活!');
require('./jump-to-definition')(context); // 跳转到定义
};
/**
* 插件被释放时触发
*/
exports.deactivate = function () {
console.log('扩展“wx-minipro-helper”已被释放!');
};
extension.js引⼊的jump-to-definition⽂件,是跳转功能的具体实现,我为这部分代码写了详尽的注释, 基本原理就是VSCode提供了光标点击事件的⼀些属性,如点击了什么⽂件,点击了那⼀⾏,点击了⽂件中的什么⽂本,根据这些信息可以拿到跳转路径,然后调⽤vscode提供的跳转api,就能实现定义跳转功能
const vscode = require('vscode');
const path = require('path');
const fs = require('fs');
const util = require('./utils');
/**
* 查⽂件定义的provider,匹配到了就return⼀个location,否则不做处理
* @param {*} document
* @param {*} position
* @param {*} token
*/
function provideDefinition(document, position, token) {
// 获取⼯作⽬录
const projectPath = ProjectPath(document);
// 获取光标所在⾏的⽂档路径
const { fileName } = document;
// 获取光标所在⾏的⽂档夹路径
const workDir = path.dirname(fileName);
/
/ 获取光标所在位置的双引号范围内的⽂本路径,并去除空格,引号,逗号
let textPath = WordRangeAtPosition(position, /[\'\"](.*?)[\'\"]/g));
textPath = place(/,|\s|\'|\"/gi, '');
// 获取光标所在⾏的信息
const line = document.lineAt(position);
// 如果跳转⾏存在import字样,屏蔽掉这个⼯具的跳转功能,因为会与开发编辑⼯具的跳转功能冲突
if (includes('import')) return null;
// wxml⽂件中标签上bindTap绑定⽅法跳转, 因为在wxml⽂件中,获取到的光标所在处的⽂本是整个⽂档,所以要修改
// 光标所在⾏截取出来的字符串,没有.或/等路径符
if (/\.wxml$/.test(fileName) && !/[\/\.]/.test(textPath)) {
// const textLine = document.lineAt(position);
const wordRange = WordRangeAtPosition(position, /[\w|\-]+\b/);
// const tag = (atch(/(?<=<\/?)[\w|\-]+\b/) || [])[0];
const word = Text(wordRange);
// console.log(wordRange, word, tag, textPath);
// 在wxml⽂件对应的js⽂件中,查⽅法名所在的⾏号和列数
const filePath = place(/\.wxml$/, '.js');
const pos = util.findStrInFile({ filePath, str: word});
const { row, col } = pos;
// 判断是否到
if (row != 0 && col != 0) {
return new vscode.Location(vscode.Uri.file(filePath), new vscode.Position(row, col));
}
return [];
}
// 项⽬定制需求--ajax url跳转,不通⽤
// 检测光标所在⾏的字符串中是否有ajax('')字样
const ajaxReg1 = new RegExp(`ajax\\(\\s*.*('|")${textPath}('|"),?`, 'gm');
//  检测光标所在⾏的字符串中是否有yield call(ajax字样
const ajaxReg2 = new RegExp(`yield call\\(ajax\\, ?('|")${textPath}('|"), ?`, 'gm');
const json = Text();
if (st(json) || st(json)) {
// webapp项⽬
const destPath1 = `${projectPath}/src/config/api.js`;
// ⼩程序项⽬
const destPath2 = `${projectPath}/config/api-config.js`;
let destPath = '';
if (fs.existsSync(destPath1)) {
destPath = destPath1;
} else if (fs.existsSync(destPath2)) {
destPath = destPath2;
}
// ⽂件存在
if (fs.existsSync(destPath)) {
try {
// 查关键字在⽂件中的⾏号和列号
const pos = util.findStrInFile({ filePath:destPath, str: textPath });
const { row, col } = pos;
return new vscode.Location(vscode.Uri.file(destPath), new vscode.Position(row, col));
} catch (error) {
vscode.window.showErrorMessage('%c error:', 'color: #0e93e0;background: #aaefe5;', JSON.stringify(error));
return [];
}
}
return []
}
// ⼩程序app.json跳转逻辑⽐较特殊,存在分包路径,如果是分包路径,要拼接⼦包root路径部分
if (fileName.includes('app.json')) {
const json = JSON.Text());
// 分包路径判断
let isSubPath = json.subpackages.some((item) => {
if (item.pages.includes(textPath)) {
// 如果是分包路径,则要拼接分包的root路径部分
textPath = `/${}/${textPath}`;
return true;
}
return false;
});
// 如果是正常页⾯路径,要在页⾯路径前⾯加绝对路径符号/
if (!isSubPath) {
textPath = `/${textPath}`;
}
8.0怎么更新}
// 如果没有⽂件后缀,说明是json,或者js,jsx⽂件中的navigateTo跳转,默认跳转到对应页⾯的wxml⽂件
const reg = /\.\w+$/;
let isNoSuffix = !(textPath);
// 没有⽂件后缀,添加默认的跳转⽂件类型
if (isNoSuffix) {
textPath = `${textPath}.wxml`;
}
let jumpPath;
if (textPath.startsWith('/')) {
// 为绝对路径时-直接拼接⼯程路径+截取的路径字符串
jumpPath = path.join(projectPath, textPath);
} else {
// 为相对路径时-转换成绝对路径
jumpPath = solve(workDir, textPath);
}
console.log('====== 进⼊ provideDefinition ⽅法 ======');
console.log('projectPath=%s ', projectPath); // 当前⼯程⽬录
console.log('document:', document); // ⽂档对象
console.log('position:', position); // 位置
// console.log('token=%o', token); // 分词
console.log('line:', line); // 当前光标所在⾏
console.log('fileName=%s ', fileName); // 当前⽂件名
console.log('workDir=%s', workDir); // 当前⽂件所在⽬录
console.log('textPath=%s', textPath); // 跳转短路径
console.log('jumpPath=%s ', jumpPath); // 跳转路径
// 路径不存在则不跳转
if (!jumpPath || !fs.existsSync(jumpPath)) {
return null;
} else {
return new vscode.Location(vscode.Uri.file(jumpPath), new vscode.Position(0, 0));
}
}
// 注册如何实现跳转到定义,第⼀个参数表⽰仅对js,jsx,ts,tsx,json,wxml⽂件⽣效
context.subscriptions.push(
isterDefinitionProvider(
{ pattern: '**/*.{ts,js,jsx,tsx,json,wxml}' },
{
provideDefinition,
}
)
);
};
step5  调试VSCode 扩展,按下F5, VSCode会启动⼀个新的窗⼝,在新窗⼝调试功能,在旧窗⼝看调试⽇志,下图的第⼀张图是旧窗⼝,第⼆张图是新窗⼝
step6  扩展开发完成后,就可以准备发布了。⾸先你需要注册⼀个发布账号,填写各种注册信息,注册完之后,安装官⽅发布脚本,然后执⾏打包脚本命令
E:\study\wx-minipro-helper>vsce package
INFO Detected presense of yarn.lock. Using 'yarn' instead of 'npm' (to override this pass '--no-yarn' o
n the command line).
DONE Packaged: E:\study\wx-minipro-helper\wx-minipro-helper-0.0.3.vsix (10 files, 23.06KB)
step7 在vscode应⽤市场发布扩展,刚才⽣成的扩展wx-minipro-helper-0.0.3.vsix
扩展的更新⽅法
避坑指南
1. VSCode 扩展的js⽂件,只要有⼀处报错,调试控制台就没有任何⽇志输出,不好排查错误。新⼿这个时候很茫然,惊慌失措。不知道哪⾥出了问题。看了这篇⽂章之后,你要淡定⼀些。
2. package.json中的vscode版本很重要,不能太⾼,我⽤yo code⽣成的初始项⽬,vscode的版本配置是 "vscode": "^1.51.0",结果我发现这个扩展只能在VSCode编辑器中使⽤,不能在开发者⼯具中使⽤,
我猜测可能是开发者⼯具只⽀持vscode⼯具包的低级版本。我把vscode的版本改成下⾯的配置后,在开发者⼯具中也可以使⽤了。
"engines": {
"vscode": "^1.5.0" // 依赖的vscode版本
},
3.在不同的⽂件类型中,WordRangeAtPosition(position)获取到的光标所在处的字符串不⼀样,在json⽂件中,能拿到双引号之间的全部内容,在js,jsx,ts,tsx,wxml⽂件中,只能拿到光标所在处的哪个单词,所以要⽤正则表达式,取出跳转路径。
4.安装的同类插件太多了,插件之间会打架,出现如下这种情况,不进⾏跳转,处理⽅法就是修改下载到本地的插件源代码,屏蔽掉⾃⼰插件或别⼈插件的同类功能。
最后
本⽂实现的⼩程序扩展插件代码托管在码云,
这个扩展已经发布到vscode 应⽤扩展市场,输⼊wx-minipro-helper就可以搜到
参考⽂章
[1]
[2]