Preface
GithubBlog:https://github.com/Nealyang/PersonalBlog/issues/99
The background is like this:
With the provision of scaffolding, as well as the functional packaging of new pages and modules.
After all, provides an extra layer of specification, and an extra layer of constraints. and the essence of the architecture is to allow developers to focus more on business development without worrying about other things. For example, some module configurations, asynchronous loading and even some services that have been defined and retained in the initialization architecture are hooks
etc., which are initialized by the above scaffolding.
For the above reasons, I hope to provide a set of visual operations (create a project, select dependencies, add pages, select required materials, configure material attributes, etc.). In a nutshell, for source code development, users only need to write corresponding service module assembly, without the module management structure is organized and distributed state, addition encoding service module, the other is visualization .
Because 100% of the students in the team use vscode
as a job, so naturally vscode extinction
is my first choice. The plan will provide a series of plug-ins such as project creation, new pages, module configuration, page configuration, and new modules. Follow-up phased progress, and then publish a summary. Ahem, yes, this will be a source workbench ~
Up to now, 90% of the project’s scaffolding has been basically built, and here is the first stage summary.
Achievement display
extensions
folder is the folder of the vscode
plug-in, the packages
folder is for storing public components, scripts
is the script for publishing, building, and developing, and the others are some project configurations.
Of course, the main thing here is not the display of product functions, quack~
packages.json scripts
"scripts": {
"publish": "lerna list && publish:package",
"publish-beta": "lerna list && npm run publish-beta:package",
"start":"tnpm run start:packages && tnpm run start:extensions",
"start:packages": "tnpm run setup:packages && tnpm run packages:watch",
"start:extensions":"tnpm run extensions:link",
"commit": "git-cz",
"env": "node ./scripts/env.js",
"packages:link": "lerna link",
"packages:install": "rm -rf node_modules && rm -rf ./packages/*/node_modules && rm -rf ./packages/*/package-lock.json && SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ yarn install --registry=http://registry.npm.taobao.org",
"packages:clean": "rm -rf ./packages/*/lib",
"packages:watch": "ts-node ./scripts/watch.ts",
"packages:build": "npm run packages:clean && ts-node ./scripts/build.ts",
"setup:packages": "npm run packages:install && lerna clean --yes && npm run packages:build && npm run packages:link ",
"publish-beta:package": "ts-node ./scripts/publish-beta-package.ts",
"publish:package": "ts-node ./scripts/publish-package.ts",
"extensions:install": " rm -rf ./extensions/*/node_modules && rm -rf ./extensions/*/package-lock.json && rm -rf ./extensions/*/web/node_modules && rm -rf ./extensions/*/web/package-lock.json && ts-node ./scripts/extension-deps-install.ts",
"extensions:link": "ts-node ./scripts/extension-link-package.ts"
}
scripts
has not been added completely. The current development direct npm start
releases packages
as npm run publish-beta:package
and npm run publish:package
respectively. There is also a summary of publish
Architecture selection
At present, it is to pmCli
into plug-ins, and then visually replace the related operations on the architecture configuration during the coding process. Therefore, there must not be only one plug-in, but a operation set based on the source code architecture: more than extensions
. There are many similar functional packages in the plug-in. Such as from gitlab
read the file base, vscode
and WebView
communication, the AST
substantially encapsulated the like, it is inevitable to rely very much packages
, to develop and provide unified management of collection efficiency inevitably occur based lerna
of monorepo
project structure.
I won’t say much about some of the pits of lerna, mainly because I just read most of the practical articles and official documents on the market, lacking some of my own practice (after all, I feel that more research can’t solve the big pain points, so I don’t want to spend it. Energy) The final monorepo
is implemented based on yarn workspace
, through lerna link
to soft chain package
, lerna
release package
more tasteless, just refer to App works
to write some packaged and released to pre-release, online scripts.
The project workflow and coding constraints are through conventional configurations such as husky
, lint-staged
, git-cz
, eslint
, prettier
The code uses ts
, so extensions
and packages
. Here you can extract the common parts and place them in the project root directory (as shown in the project directory screenshot above).
practice
I won’t talk about it here to initialize lerna init
and lerna create xxx
Anyway, after package.json
it is a directory structure packages
and 060d015e183507 files.
Project structure
package structure
The above structure description is in the picture
Script package
scripts
folder is placed in the root directory of the project, which stores some scripts for release, development, and dependent installation.
getPakcageInfo.ts
Used to obtain relatedpublish
informationpackages
Among them,shouldPublish
compares the localversion
with the onlineversion
, and judges that the master needs to executepublish
/*
* @Author: 一凨
* @Date: 2021-06-07 18:47:32
* @Last Modified by: 一凨
* @Last Modified time: 2021-06-07 19:12:28
*/
import { existsSync, readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import { getLatestVersion } from 'ice-npm-utils';
const TARGET_DIRECTORY = join(__dirname, '../../packages');
// 定义需要获取到的信息结构
export interface IPackageInfo {
name: string;
directory: string;
localVersion: string;
mainFile: string; // package.json main file
shouldPublish: boolean;
}
// 检查 package 是否 build 成功
function checkBuildSuccess(directory: string, mainFile: string): boolean {
return existsSync(join(directory, mainFile));
}
// 判断线上最新version是否和本地 version 相同
function checkVersionExists(pkg: string, version: string): Promise<boolean> {
return getLatestVersion(pkg)
.then((latestVersion) => version === latestVersion)
.catch(() => false);
}
export async function getPackageInfos ():Promise<IPackageInfo[]>{
const packageInfos: IPackageInfo[] = [];
if (!existsSync(TARGET_DIRECTORY)) {
console.log(`[ERROR] Directory ${TARGET_DIRECTORY} not exist!`);
} else {
// 拿到所有packages 目录,再去遍历其 package.json
const packageFolders: string[] = readdirSync(TARGET_DIRECTORY).filter((filename) => filename[0] !== '.');
console.log('[PUBLISH] Start check with following packages:');
await Promise.all(
packageFolders.map(async (packageFolder) => {
const directory = join(TARGET_DIRECTORY, packageFolder);
const packageInfoPath = join(directory, 'package.json');
// Process package info.
if (existsSync(packageInfoPath)) {
const packageInfo = JSON.parse(readFileSync(packageInfoPath, 'utf8'));
const packageName = packageInfo.name || packageFolder;
console.log(`- ${packageName}`);
// 从 package.json 中取信息 返回
try {
packageInfos.push({
name: packageName,
directory,
localVersion: packageInfo.version,
mainFile: packageInfo.main,
// If localVersion not exist, publish it
shouldPublish:
checkBuildSuccess(directory, packageInfo.main) &&
!(await checkVersionExists(packageName, packageInfo.version)),
});
} catch (e) {
console.log(`[ERROR] get ${packageName} information failed: `, e);
}
} else {
console.log(`[ERROR] ${packageFolder}'s package.json not found.`);
}
}),
);
}
return packageInfos;
}
The explanation of the code is in the comments. What the core does is read the information in each package
of package.json
packages
, and then compose the required format and return it for publishing.
publish-beta-package
/*
* @Author: 一凨
* @Date: 2021-06-07 18:45:51
* @Last Modified by: 一凨
* @Last Modified time: 2021-06-07 19:29:26
*/
import * as path from 'path';
import * as fs from 'fs-extra';
import { spawnSync } from 'child_process';
import { IPackageInfo, getPackageInfos } from './fn/getPackageInfos';
const BETA_REG = /([^-]+)-beta\.(\d+)/; // '1.0.0-beta.1'
interface IBetaPackageInfo extends IPackageInfo {
betaVersion: string;
}
function setBetaVersionInfo(packageInfo: IPackageInfo): IBetaPackageInfo {
const { name, localVersion } = packageInfo;
let version = localVersion;
if (!BETA_REG.test(localVersion)) {
// 如果 localVersion 不是 beta version,则盘他!
let betaVersion = 1;
// 获取package 的 dist-tag 相关信息
const childProcess = spawnSync('npm', ['show', name, 'dist-tags', '--json'], {
encoding: 'utf-8',
});
const distTags = JSON.parse(childProcess.stdout || "{}") || {};
const matched = (distTags.beta || '').match(BETA_REG);
// 1.0.0-beta.1 -> ["1.0.0-beta.1", "1.0.0", "1"] -> 1.0.0-beta.2
if (matched && matched[1] === localVersion && matched[2]) {
// 盘 version,+1
betaVersion = Number(matched[2]) + 1;
}
version += `-beta.${betaVersion}`;
}
return Object.assign({}, packageInfo, { betaVersion: version });
}
// 将矫正后的 betaVersion 写到对应 package.json 中
function updatePackageJson(betaPackageInfos: IBetaPackageInfo[]): void {
betaPackageInfos.forEach((betaPackageInfo: IBetaPackageInfo) => {
const { directory, betaVersion } = betaPackageInfo;
const packageFile = path.join(directory, 'package.json');
const packageData = fs.readJsonSync(packageFile);
packageData.version = betaVersion;
for (let i = 0; i < betaPackageInfos.length; i++) {
const dependenceName = betaPackageInfos[i].name;
const dependenceVersion = betaPackageInfos[i].betaVersion;
if (packageData.dependencies && packageData.dependencies[dependenceName]) {
packageData.dependencies[dependenceName] = dependenceVersion;
} else if (packageData.devDependencies && packageData.devDependencies[dependenceName]) {
packageData.devDependencies[dependenceName] = dependenceVersion;
}
}
fs.writeFileSync(packageFile, JSON.stringify(packageData, null, 2));
});
}
// npm publish --tag=beta 发布
function publish(pkg: string, betaVersion: string, directory: string): void {
console.log('[PUBLISH BETA]', `${pkg}@${betaVersion}`);
spawnSync('npm', ['publish', '--tag=beta'], {
stdio: 'inherit',
cwd: directory,
});
}
// 入口文件
console.log('[PUBLISH BETA] Start:');
getPackageInfos().then((packageInfos: IPackageInfo[]) => {
const shouldPublishPackages = packageInfos
.filter((packageInfo) => packageInfo.shouldPublish)
.map((packageInfo) => setBetaVersionInfo(packageInfo));
updatePackageJson(shouldPublishPackages);
// Publish
let publishedCount = 0;
const publishedPackages = [];
shouldPublishPackages.forEach((packageInfo) => {
const { name, directory, betaVersion } = packageInfo;
publishedCount++;
// 打印此次发布的相关信息
console.log(`--- ${name}@${betaVersion} ---`);
publish(name, betaVersion, directory);
publishedPackages.push(`${name}:${betaVersion}`);
});
console.log(`[PUBLISH PACKAGE BETA] Complete (count=${publishedCount}):`);
console.log(`${publishedPackages.join('\n')}`);
});
The basic functions are in the comments (not to repeat this sentence), summarize the function of the script:
- Get all local packageInfo information
- Compare online (released) information and correct the version information required for this release
- Add (write) the corrected version information to package.json in the local corresponding package
- Call the script and execute the release
publish-package
is very simple, and it is relatively simple to write, that is, callnpm publish
. Of course, some basic online verification is also required, such as the above-mentionedshouldPublish
. I won't go into details!Note that, when released, need attention landed (
npm whoami
) and if you are using@xxx/
naming of it, pay attention to the correspondingorganization
rights
watch
Mainly rely on the ability of nsfw to monitor local files. has changes, we compile and we are done!
/*
* @Author: 一凨
* @Date: 2021-06-07 20:16:09
* @Last Modified by: 一凨
* @Last Modified time: 2021-06-10 17:19:05
*/
import * as glob from 'glob';
import * as path from 'path';
import * as fs from 'fs-extra';
import { run } from './fn/shell';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const nsfw = require('nsfw');
async function watchFiles(cwd, ext) {
const files = glob.sync(ext, { cwd, nodir: true });
const fileSet = new Set();
/* eslint no-restricted-syntax:0 */
for (const file of files) {
/* eslint no-await-in-loop:0 */
await copyOneFile(file, cwd);
fileSet.add(path.join(cwd, file));
}
const watcher = await nsfw(cwd, (event) => {
event.forEach((e) => {
if (
e.action === nsfw.actions.CREATED ||
e.action === nsfw.actions.MODIFIED ||
e.action === nsfw.actions.RENAMED
) {
const filePath = e.newFile ? path.join(e.directory, e.newFile!) : path.join(e.directory, e.file!);
if (fileSet.has(filePath)) {
console.log('non-ts change detected:', filePath);
copyOneFile(path.relative(cwd, filePath), cwd);
}
}
});
});
watcher.start();
}
watchFiles(path.join(__dirname, '../packages'), '*/src/**/!(*.ts|*.tsx)').catch((e) => {
console.trace(e);
process.exit(128);
});
// 在这之上的代码都是为了解决 tsc 不支持 copy 非 .ts/.tsx 文件的问题
async function tscWatcher() {
await run('npx tsc --build ./tsconfig.json -w');
}
tscWatcher();
async function copyOneFile(file, cwd) {
const from = path.join(cwd, file);
const to = path.join(cwd, file.replace(/src\//, '/lib/'));
await fs.copy(from, to);
}
extensions-deps-install
Because our workspace
is in the packages
directory, so for the plug-ins under extensions
web
page, we can't directly install
yarn
, and freely provide a script for plug-in installation dependencies. actually went to the project directory to execute npm i
import * as path from 'path';
import * as fse from 'fs-extra';
import * as spawn from 'cross-spawn';
export default function () {
const extensionsPath = path.join(__dirname, '..', '..', 'extensions');
const extensionFiles = fse.readdirSync(extensionsPath);
const installCommonds = ['install'];
if (!process.env.CI) { // 拼接参数
installCommonds.push('--no-package-lock');
installCommonds.push('--registry');
installCommonds.push(process.env.REGISTRY ? process.env.REGISTRY : 'http://registry.npm.taobao.org');
}
for (let i = 0; i < extensionFiles.length; i++) {
// 遍历安装,如果有 web 目录,则继续安装 web 页面里的依赖
const cwd = path.join(extensionsPath, extensionFiles[i]);
// eslint-disable-next-line quotes
console.log("Installing extension's dependencies", cwd);
spawn.sync('tnpm', installCommonds, {
stdio: 'inherit',
cwd,
});
const webviewPath = path.join(cwd, 'web');
if (fse.existsSync(webviewPath)) {
// eslint-disable-next-line quotes
console.log("Installing extension webview's dependencies", webviewPath);
spawn.sync('tnpm', installCommonds, {
stdio: 'inherit',
cwd: webviewPath,
});
}
}
}
Note thatscripts
are all ts codes, so usets-node
to executenpmScripts
extension-link-package
Delete the local related package and let it recursively find the package after the corresponding soft chain upwards (application level)
import * as path from 'path';
import * as fse from 'fs-extra';
import { run } from './fn/shell';
(async function () {
const extensionsPath = path.join(__dirname, '../extensions');
const extensionFiles = await fse.readdir(extensionsPath);
// 获取 extensions 下的插件列表,挨个遍历执行 remove
return await Promise.all(
extensionFiles.map(async (extensionFile) => {
const cwd = path.join(extensionsPath, extensionFile);
if (fse.existsSync(cwd)) {
// link packages to extension
if (!process.env.CI) {
await removePmworks(cwd);
}
const webviewPath = path.join(cwd, 'web');
if (fse.existsSync(webviewPath)) {
// link packages to extension webview
if (!process.env.CI) {
await removePmworks(webviewPath);
}
}
}
}),
);
})().catch((e) => {
console.trace(e);
process.exit(128);
});
// 删除 @pmworks 下的依赖
async function removePmworks(cwd: string) {
const cwdStat = await fse.stat(cwd);
if (cwdStat.isDirectory()) {
await run(`rm -rf ${path.join(cwd, 'node_modules', '@pmworks')}`);
}
}
Small summary
The core scripts are as above, in fact, they are relatively simple and direct functions. I haven't written anything about the release of extensions. In fact, it can be borrowed (copied) from apps (copied). Wait for the subsequent release of the plug-in to add it.
After a project has completed its infrastructure, it can basically be started. Here I take the creation of the project as an example (focus on the infrastructure part, without specific explanations on the plug-in function and implementation, and then summarize it in the second stage).
vscode extensions (vscode-webview package example)
We use yo code
to initialize the plug-in we want to write in the extensions
For specific basic knowledge, refer to the official document: https://code.visualstudio.com/api
After the above, we have the basic structure of a project, a series of package management, we can already enter our development stage.
After all, our plug-in is a series of operations for visualization, so vscode
must not satisfy us. We need an operation interface: webView
. The picture above is an webView
plug-in:
Common-xxx(utils)
is responsible for some general function packages at the entire project levelExtension-utils
is some method library extracted for a certain plug-in. For example,project-utils
iscreateProject
initializes the project, similar to acontroller
extension-service
is thevscode
andwebView
communications, as the name implies:service
The above is a bit MVC
difference from the traditional 060d015e184207 is that there are two views here: vscode-extension
and extension-webview
Give a chestnut! Here is an example of initializing a project professor~
For basic concepts related to vscode extension with WebView, please see here: https://code.visualstudio.com/api/extension-guides/webview
WebView
In fact, there is not much to prepare for WebView, that is, to prepare the three front-end HTML, JavaScript and css.
Here is the project initialized from the scaffolding of ice I used: npm init ice
Then modify build.json
in outputDir
configuration, and specify mpa
mode
{
"mpa": true,
"vendor": false,
"publicPath": "./",
"outputDir": "../build",
"plugins": [
[
"build-plugin-fusion",
{
"themePackage": "@alifd/theme-design-pro"
}
],
[
"build-plugin-moment-locales",
{
"locales": [
"zh-cn"
]
}
],
"@ali/build-plugin-ice-def"
]
}
After coding the code, you can get our three big pieces.
For more documentation about ice, please go to the official documentation
Extensions
import * as vscode from 'vscode';
import { getHtmlFroWebview, connectService } from "@pmworks/vscode-webview";
import { DEV_WORKS_ICON } from "@pmworks/constants";
import services from './services';
export function activate(context: vscode.ExtensionContext) {
const { extensionPath } = context;
let projectCreatorPanel: vscode.WebviewPanel | undefined;
const activeProjectCreator = () => {
const columnToShowIn = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined;
if (projectCreatorPanel) {
projectCreatorPanel.reveal(columnToShowIn)
} else {
projectCreatorPanel = vscode.window.createWebviewPanel('BeeDev', '初始化源码架构', columnToShowIn || vscode.ViewColumn.One, {
enableScripts: true,
retainContextWhenHidden: true,
});
}
projectCreatorPanel.webview.html = getHtmlFroWebview(extensionPath, 'projectcreator', false);
projectCreatorPanel.iconPath = vscode.Uri.parse(DEV_WORKS_ICON);
projectCreatorPanel.onDidDispose(
() => {
projectCreatorPanel = undefined;
},
null,
context.subscriptions,
);
connectService(projectCreatorPanel, context, { services });
}
let disposable = vscode.commands.registerCommand('devworks-project-creator.createProject.start', activeProjectCreator);
context.subscriptions.push(disposable);
}
export function deactivate() { }
Here are also regular operations, register commands and related callbacks, and initialize WebView
. Here we talk about getHtmlFroWebview
/**
* 给本地资源带上安全协议
* @param url 本地资源路径
* @returns 带有 vscode-resource 协议的安全路径
*/
function originResourceProcess(url: string) {
return vscode.Uri.file(url).with({ scheme: 'vscode-resource' });
}
export const getHtmlFroWebview = (
extensionPath: string,
entryName: string,
needVendor?: boolean,
cdnBasePath?: string,
extraHtml?: string,
resourceProcess?: (url: string) => vscode.Uri,): string => {
resourceProcess = resourceProcess || originResourceProcess;
const localBasePath = path.join(extensionPath, 'build');
const rootPath = cdnBasePath || localBasePath;
const scriptPath = path.join(rootPath, `js/${entryName}.js`);
const scriptUri = cdnBasePath ?
scriptPath :
resourceProcess(scriptPath);
const stylePath = path.join(rootPath, `css/${entryName}.css`);
const styleUri = cdnBasePath ?
stylePath :
resourceProcess(stylePath);
// vendor for MPA
const vendorStylePath = path.join(rootPath, 'css/vendor.css');
const vendorStyleUri = cdnBasePath
? vendorStylePath
: resourceProcess(vendorStylePath);
const vendorScriptPath = path.join(rootPath, 'js/vendor.js');
const vendorScriptUri = cdnBasePath
? vendorScriptPath
: resourceProcess(vendorScriptPath);
// Use a nonce to whitelist which scripts can be run
const nonce = getNonce();
return `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<title>Iceworks</title>
<link rel="stylesheet" type="text/css" href="${styleUri}">
${extraHtml || ''}
` +
(needVendor ? `<link rel="stylesheet" type="text/css" href="${vendorStyleUri}" />` : '') +
`
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="ice-container"></div>
` +
(needVendor ? `<script nonce="${nonce}" src="${vendorScriptUri}"></script>` : '') +
`<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}
function getNonce(): string {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
The method is located at packages/vscode-webview/vscode.ts
. In fact, is to get a piece of html vscode
protocol to the local resources. Support vendor
, extraHtml
etc.
Up to now, we can evoke our WebView in vscode.
Communication
Then the problem of communication between vscode and WebView is solved. The communication here is very similar to pubSub:
Plug-in message to WebView
panel.webview.postMessage({text:"你好,这里是 vscode 发送过来的消息"});
webview accepts messages
window.addEventListener('message',event=>{ const message = event.data; console.log(`WebView 接受到的消息:${message}`); })
webview sends a message to the plug-in
vscode.postMessage({text:"你好,这是 webView 发送过来的消息"});
Plug-in acceptance
panel.webview.onDidReceiveMessage(msg=>{ console.log(`插件接受到的消息:${msg}`) },undefined,context.subscriptions);
This communication mechanism is too fragmented. In actual projects, webView
more similar to our view
layer. So theoretically only service
to use 060d015e184f7c to call the controller
interface to complete the underlying operation and tell me the result can be :
For example, when creating a project, you need to let the user choose to create a directory. The click handle of the select button on the HTML page should be as follows:
const getAppPath = async () => {
const projectPath = await callService('project', 'getFolderPath', 'ok');
setAppPath(projectPath);
};
callService
the first parameter as service
class, the second method as the class name which is required to call, their corresponding parameters of the subsequent process.
Just as above, we encapsulate a callService
method:
// packages/vscode-webview/webview.ts
// @ts-ignore
export const vscode = typeof acquireVsCodeApi === 'function' ? acquireVsCodeApi() : null;
export const callService = function (service: string, method: string, ...args) {
// 统一 return promise,统一调用方式
return new Promise((resolve, reject) => {
// 生成对应的 eventId
const eventId = setTimeout(() => { });
console.log(`WebView call vscode extension service:${service} ${method} ${eventId} ${args}`);
// 收到 vscode 发来消息,一般为处理后 webView 的需求后
const handler = event => {
const msg = event.data;
console.log(`webview receive vscode message:}`, msg);
if (msg.eventId === eventId) {// 去定时对应的 eventID,说明此次通信结束,可以移除(结束)此次通信了
window.removeEventListener('message', handler);
msg.errorMessage ? reject(new Error(msg.errorMessage)) : resolve(msg.result);
}
}
// webview 接受 vscode 发来的消息
window.addEventListener('message', handler);
// WebView 向 vscode 发送消息
vscode.postMessage({
service,
method,
eventId,
args
});
});
}
webview
layer has completed the encapsulation of the time request for sending time, receiving time request, and canceling after acceptance ( removeListener
). So we give in to extension
add the corresponding webView
need service.methodName
job.
Here we encapsulate a method called connectService.
connectService(projectCreatorPanel, context, { services });
The above projectCreatorPanel
is to create out WebviewPanel
"instance", and services will be appreciated that a plurality of object classes comprising
const services = {
project:{
getFolderPath(...args){
//xxx
},
xxx
},
xxx:{}
}
The specific connectService method is as follows:
export function connectService(
webviewPanel: vscode.WebviewPanel,
context: vscode.ExtensionContext,
options: IConnectServiceOptions
) {
const { subscriptions } = context;
const { webview } = webviewPanel;
const { services } = options;
webview.onDidReceiveMessage(async (message: IMessage) => {
const { service, method, eventId, args } = message;
const api = services && services[service] && services[service][method];
console.log('onDidReceiveMessage', message);
if (api) {
try {
const fillApiArgLength = api.length - args.length;
const newArgs = fillApiArgLength > 0 ? args.concat(Array(fillApiArgLength).fill(undefined)) : args;
const result = await api(...newArgs, context, webviewPanel);
console.log('invoke service result', result);
webview.postMessage({ eventId, result });
} catch (err) {
console.error('invoke service error', err);
webview.postMessage({ eventId, errorMessage: err.message });
}
} else {
vscode.window.showErrorMessage(`invalid command ${message}`);
}
}, undefined, subscriptions);
}
The code above is relatively simple, it is registered listener function, then just listen to WebView
post
over message
, to pick up the corresponding services
an under service
of method
to execute and pass WebView
pass over the parameters .
The extension services are introduced here
The @pmworks/project-service
package only encapsulates some basic method calls. The core processing logic such as downloading the corresponding gitRpo
, parsing local files, etc. are all performed in the corresponding extension-utils
. service can only be called.
Little problem
The basic process encapsulation has been completed as above, and the rest is the preparation of specific logic. But in actual development, the web page needs to get the parameters passed in by vscode, and in web page development, the vscode plug-in cannot read the uncompiled code. How to solve it?
encapsulates a layer of callService in webView for local web page development
Follow-up outlook
Up to now, I have basically finished the introduction of some developments in addition to the business work in the past two weeks. Next, you need to make up for the related APIs of the vscode plug-in and get ready to do it. Of course, before this, another very, very urgent task is to upgrade the source code structure compiled last year to align some of the capabilities of the current rax system in the group.
In the development of this plug-in system (BeeDev source code workbench), the follow-up also needs:
- Initialize the source code architecture
- Create a page, drag and drop related H5 source code materials (requires the entire material background) to generate an initialization page
- Create modules, visually configure module loading categories, etc.
If you have more energy, you actually need a node
backend to get through the server and local capabilities (it's just a desktop application~)
Okay, don’t be YY, let’s go with the sauce first~ the next milestone will be summarized~~
As for the project source code. . .
references
- appworks:https://appworks.site/
- vscode extension api:https://code.visualstudio.com/api
- monorepo&leran:https://github.com/lerna/lerna
other
Pay attention to the WeChat public account [Selected Full Stack Front End], push selected articles every day~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。