“This is the second day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

Back in The book, in Assembly of Heroes, we identified the stack of technology to use for the Stitch Monster project, but the overall development process was not silky smooth.

We will make some changes to the project to make the development more smooth and intelligent.

The code to build

The plug-in part

The visual studio Code development scaffolding Yo, a plug-in development project (TypeScript) generated by default, provides several commands in package.json for building project debug, published code.

"scripts": {
  "vscode:prepublish": "npm run compile".// VSce publish executes the build before publishing
  "compile": "tsc -p ./".// Build code
  "watch": "tsc -watch -p ./".// Listen for code changes to build. },Copy the code

The current build code is relatively simple, directly using the TSC command to convert.ts files into.js output to the out directory. As the plug-in code gets more complex and more packages are introduced, this doesn’t make much sense.

Fortunately, there are a number of packaged build tools that solve this problem, such as WebPack, EsBuild, rollup, and more. Now we will use Webpack to transform our project.

First we introduced webPack-related NPM packages in the project

yarn add -D webpack webpack-cli node-loader
Copy the code

Create a new webpack.config.js file that looks something like this:

"use strict";

const path = require("path");

/ * *@type {import('webpack').Configuration}* /
const config = {
  target: "node"./ / vscode plug-in run on the Node. Js environment 📖 - > https://webpack.js.org/configuration/node/
  entry: "./src/extension.ts"./ / plug-in entry documents 📖 - > https://webpack.js.org/configuration/entry-context/
  output: {
    / / packed file stored in the 'dist folder (please refer to the package. The json), 📖 - > https://webpack.js.org/configuration/output/
    path: path.resolve(__dirname, "dist"),
    filename: "extension.js".libraryTarget: "commonjs2".devtoolModuleFilenameTemplate: ".. /[resource-path]"
  },
  devtool: "source-map".externals: {
    vscode: "commonjs vscode" // vscode-module is a temporary directory for hot updates, so it should be excluded. Add other here should not be webpack packaged files, 📖 - > https://webpack.js.org/configuration/externals/
  },
  resolve: {
    / / support read the TypeScript and JavaScript files, 📖 - > https://github.com/TypeStrong/ts-loader
    extensions: [".ts".".js".".node"]},module: {
    rules: [{test: /\.ts$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "ts-loader"}]}, {test: /\.node$/,
        exclude: /node_modules/,
        loader: "node-loader".options: {name: "native.node",}},]}};module.exports = config;
Copy the code

Specify the entry as./ SRC /extension.ts and the output directory as the dist directory in the current directory.

In addition, we have introduced a.node file loader that copies the referenced.node file to the./dist directory and names it native. Node. When WebPack comes across a.node file when parsing code, it treats it as a.js file, reads its contents, and iterates over references. Unfortunately, the.node file is a binary file, and webpack will report an error that doesn’t look like what we want.

I then specify the main field in package.json as./dist/extension.js and modify the commands responsible for building the code:

"scripts": {
  "vscode:prepublish": "webpack --mode production".// Build production code
  "compile": "webpack --mode none".// Build debug, test code
  "watch": "webpack --mode none --watch".// Listen and build. },Copy the code

.vscode/tasks.json also needs to be changed:

"problemMatcher": "$tsc-watch"Left left left left left left left left down down down down down down down left left left left left left left left down down down down down down down"problemMatcher": [
  "$ts-webpack-watch"."$tslint-webpack-watch"
]
Copy the code

Now that we’re done with the build aspect of the plug-in project, let’s take a look at some of the improvements you can make to the compilation aspect of Rust & Neon code

Rust & Neon section

For plug-in development, Rust provides support in the form of a.node module compiled with the following commands:

. env.sh && neon build
Copy the code

Every time you change the Rust code, you need to re-compile so that the plug-in gets the latest.node file, which is really nei.

Can I have it automatically compile every time I change it? You can! Powerful VSCode, nothing can’t.

Even if VSCode itself doesn’t support it, there are a lot of different people out there who can create great plugins for your weird needs.

The plugin we used here is “Run on Save”, which can listen for specified file changes and supports wildcards.

Add the following code to the workspace configuration file.vscode/settings.json:

"emeraldwalk.runonsave": {
  "commands": [{"match": "native/src/lib.rs".// Neon's connection file is triggered when it is saved
      "isAsync": true."cmd": "tasks/rust-to-node.sh" // Execute the compile script
    },
    {
      "match": "native/src/public/*".Trigger when all files in the public directory are saved
      "isAsync": true."cmd": "tasks/rust-to-node.sh" // Execute the compile script]}},Copy the code

After the file is saved, the tasks/rust-to-node.sh command is automatically executed to compile a new.node file. The rust-to-node.sh file is as follows:

# Compile Neon as a.node file
# Executing.env.sh && neon build will fail
The command neon could not be found, so run the command from the actual path
. env.sh && node_modules/.bin/neon build

Create a directory to prevent moving files because there is no folder
mkdir -p src/lib/native

# Move the compiled.node file to the plug-in project
mv native/index.node src/lib/native/index.node
Copy the code

Is it possible to trigger compilation directly as a shortcut key? The answer is also ok, please put VSCode cowhet on the public screen, let’s configure.

Add the following task configuration to the tasks array in.vscode/tasks.json:

// Perform a compilation task
{
  "label": "compile"."type": "shell"."command":"tasks/rust-to-node.sh"."isBackground": true,}Copy the code

Then open Settings → Keyboard Shortcut and switch to edit mode

Added the following key bindings:

{
  "key": "cmd+r"."command": "workbench.action.tasks.runTask"."args": "compile"
}
Copy the code

This will execute the compile task with label as compile when the Command + R (Ctrl + R on Windows) shortcut is pressed.

Incurable diseases

It seems that the compiled.node file will not trigger the file modification listening of Webpack, and an exception will be reported when debugging again. After testing, just copy the new.node file to the dist directory to solve the problem. So add the command to copy the new file to rust-to-node.sh:

Copy the latest.node file to the debug directory
rm -rf dist/native.node

The end of the computer is metaphysics
cp -f src/lib/native/index.node dist/native.node
Copy the code

smart

The idea behind making this project smarter is to make the.node file feel better when you use it, and to let developers know what methods it contains.

The d.ts files in TypeScript describe the type of an object and allow the editor to provide intelligent hints, so we can write a d.ts file for the compiled.node file to make it easier to use.

However, we have to manually modify the D.TS file every time we change, which is very complicated and not intelligent. The wise Flower cat once said: laziness is the first productive force.

This tedious task had to be left to code, so I wrote a small tool to generate the corresponding D.ts file for the functions exposed by Neon’s connection layer.

Tasks /build-type.js file contents:

const fs = require('fs');
const { nodeOutput, neonLibPath } = require('./config.json');

// Parse a single parameter type
function singleParamsParse(paramsItem) {
  paramsItem.match(/@param\s\{([a-zA-z_]*)\}\s([a-zA-z0-9_]*)\s-\s([\u4e00-\u9fa5a-zA-Z0-9_ -]*)$/gm);
  const type = RegExp. $1;const name = RegExp. $2;const description = RegExp. $3;return {
    name,
    type,
    description
  };
}

// Parse a single function type
function singleFunctionTypeParse(origin) {
  origin.match(/@name\s([a-zA-z0-9_]*)[\S\s]*@returns\s\{([a-zA-Z_ -<>]*)\}/gm);
  const name = RegExp. $1;const returns = RegExp. $2;const paramsMatch = origin.match(/@param\s\{([a-zA-z0-9_]*)\}\s([a-zA-z0-9_]*)\s-\s([\u4e00-\u9fa5a-zA-Z0-9_ -]*)$/gm)?? [];const params = paramsMatch.map(singleParamsParse);
  return {
    origin,
    name,
    params,
    returns
  };
}

// Convert the parsed function type to ts
function convertType(functionInfo) {
  const { origin, name, returns, params } = functionInfo;
  const paramsString = params.map(item= > `${item.name}: ${item.type}`).join(', ');
  return `${origin}
export function ${name}(${paramsString}) :${returns}; `;
}

async function main() {
  const neonLibContent = fs.readFileSync(neonLibPath, 'utf8').toString();
  const functionMatch = neonLibContent.match(/ / \ \ * {1, 2} \ n \ r \ [* sa - zA - Z0-9 @ \ u4e00 \ u9fa5 - _ {} < >] *, *, / / gm);

  const resultContent = functionMatch.map(singleFunctionTypeParse).map(convertType).join('\n\n');

  fs.writeFileSync(`${nodeOutput}/index.d.ts`, resultContent, 'utf8');
}

main();
Copy the code

Take the file-reading function in the Neon connection layer as an example

/** * @name read_file * @description Reads the file * @param {string} path - File path * @returns {string} */
pub fn read_file(mut cx: FunctionContext) -> JsResult<JsString> {
    let path = cx.argument::<JsString>(0)? .value();Ok(cx.string(utils::public_read_file(path.as_str())))
}
Copy the code

Above the build – type. Js tool to match the comment, obtain the function name, into the house and return values, produce the following content into SRC/lib/native/index which s file

/ * * *@name read_file
* @description Read file *@param {string} path- File path *@returns {string}* /
export function read_file(path: string) :string;
Copy the code

Add execution of the build-type.js tool to rust-to-node.sh as well

Generate.d.ts files for.node files
node tasks/build-type.js
Copy the code

This will automatically generate a d.ts file every time you compile the.node file.

Using.node files in TypeScript projects requires the following import:

import * as native from './lib/native';
Copy the code

This allows you to list the functions when you use it

There is also a prompt for passing fewer parameters

There is no problem with checking the variable assignment type

Because my regular attestation is not enough, the annotation format may be slightly incorrect will lead to matching failure, so here provides a code fragment, let the annotation writing more convenient and accurate, hee hee

"neon function annotation": {
  "scope": "rust"."prefix": "na"."body": [
    "/ * *"."* @name $1"."* @description $2"."* @param {${5:string}} $3 - $4"."* @returns {${6:string}}"."* /"]."description": "Neon Function annotation template"
}
Copy the code

This code snippet can be configured in the workspace or globally, depending on your preference.

summary

“Knife grinding” to this end, complete the above configuration, has been able to more comfortable development of the suture strange project, in this process, in fact, learned a lot of work (TOU) process (LAN) skills, benefit a lot.

If you have any suggestions, please let me know in the comments section. Thank you very much.

Ok guys, that’s all for today’s share, the tide is coming in.

The original address: www.fmcat.top/posts/16366…