@tgrv/void-cli 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/bin/void.js +107 -44
- package/package.json +1 -1
- package/templates/go/README.md +52 -0
- package/templates/go/void.json +7 -2
- package/templates/rust/README.md +44 -0
- package/templates/rust/void.json +7 -2
package/README.md
CHANGED
|
@@ -14,33 +14,37 @@ npm install -g @tgrv/void-cli
|
|
|
14
14
|
|
|
15
15
|
### 1. Initialize a new application project
|
|
16
16
|
```bash
|
|
17
|
-
void init [path]
|
|
17
|
+
void init [app-path]
|
|
18
18
|
```
|
|
19
|
-
Creates standard configuration templates and installs the `@tgrv/void-runtime` engine automatically.
|
|
19
|
+
Creates standard configuration templates and installs the `@tgrv/void-runtime` engine automatically. Defaults to the current folder (`.`).
|
|
20
20
|
|
|
21
21
|
### 2. Create a new Rust or Go plugin project
|
|
22
22
|
```bash
|
|
23
23
|
void create [plugin-path]
|
|
24
24
|
```
|
|
25
|
-
Prompts for language configuration and instantiates starting templates with pre-configured SDK targets.
|
|
25
|
+
Prompts for language configuration and instantiates starting templates with pre-configured SDK targets. Defaults to the current folder (`.`).
|
|
26
26
|
|
|
27
27
|
### 3. Build a plugin locally
|
|
28
28
|
```bash
|
|
29
29
|
void build [plugin-path]
|
|
30
30
|
```
|
|
31
|
-
Compiles source files (Cargo/Go) and wraps the
|
|
31
|
+
Compiles source files (Cargo/Go) and wraps the WebAssembly binary and configured assets under the build directory configured in `void.json`. Run from a plugin root folder (defaults to current folder `.`).
|
|
32
32
|
|
|
33
33
|
### 4. Publish a plugin
|
|
34
34
|
```bash
|
|
35
35
|
void publish [plugin-path]
|
|
36
36
|
```
|
|
37
|
-
Compiles and
|
|
37
|
+
Compiles the plugin and runs `npm publish` natively from inside the built directory. Run from a plugin root folder (defaults to current folder `.`).
|
|
38
38
|
|
|
39
39
|
### 5. Install / Add a plugin
|
|
40
40
|
```bash
|
|
41
|
-
void add <plugin-name>
|
|
41
|
+
void add <plugin-name-or-build-path>
|
|
42
42
|
```
|
|
43
|
-
|
|
43
|
+
Installs a plugin into the application:
|
|
44
|
+
- **Registry:** `void add <plugin-name>` downloads the plugin from the NPM registry.
|
|
45
|
+
- **Local Development / Testing:** `void add <build-folder-path>` (e.g. `void add ../plugins/math/@void/void-math`) copies the compiled local build output directory directly.
|
|
46
|
+
|
|
47
|
+
Adds the plugin dependency automatically to `void.config.json` and `package.json`.
|
|
44
48
|
|
|
45
49
|
### 6. Remove a plugin
|
|
46
50
|
```bash
|
package/bin/void.js
CHANGED
|
@@ -100,48 +100,82 @@ function copyFolderRecursive(src, dest, replacements = {}) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
function copyNonSourceFiles(src, dest, buildOutputDir) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
103
|
+
function copyNonSourceFiles(src, dest, buildOutputDir, filesConfig, wasmFilename) {
|
|
104
|
+
if (!filesConfig || !Array.isArray(filesConfig) || filesConfig.length === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function matchGlob(filePath, pattern) {
|
|
109
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
110
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
111
|
+
|
|
112
|
+
if (normalizedPath === normalizedPattern) {
|
|
113
|
+
return true;
|
|
113
114
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
entry.name === "@tgrv" ||
|
|
118
|
-
entry.name === "target" ||
|
|
119
|
-
entry.name === ".git" ||
|
|
120
|
-
entry.name === "node_modules") {
|
|
121
|
-
continue;
|
|
115
|
+
const prefix = normalizedPattern.endsWith('/') ? normalizedPattern : normalizedPattern + '/';
|
|
116
|
+
if (normalizedPath.startsWith(prefix)) {
|
|
117
|
+
return true;
|
|
122
118
|
}
|
|
119
|
+
|
|
120
|
+
const globToRegex = (glob) => {
|
|
121
|
+
return '^' + glob
|
|
122
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
123
|
+
.replace(/\*\*/g, '___DOUBLE_STAR___')
|
|
124
|
+
.replace(/\*/g, '___SINGLE_STAR___')
|
|
125
|
+
.replace(/\?/g, '___QUESTION___')
|
|
126
|
+
.replace(/___DOUBLE_STAR___/g, '.*')
|
|
127
|
+
.replace(/___SINGLE_STAR___/g, '[^/]*')
|
|
128
|
+
.replace(/___QUESTION___/g, '.') + '$';
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const regex = new RegExp(globToRegex(normalizedPattern));
|
|
123
132
|
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
if (!normalizedPattern.includes('/')) {
|
|
134
|
+
const baseName = path.basename(normalizedPath);
|
|
135
|
+
if (regex.test(baseName)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return regex.test(normalizedPath);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function traverse(currentDir) {
|
|
144
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
129
147
|
|
|
130
|
-
//
|
|
131
|
-
if (
|
|
132
|
-
ext === ".rs" ||
|
|
133
|
-
filename === "cargo.toml" ||
|
|
134
|
-
filename === "cargo.lock" ||
|
|
135
|
-
filename === "go.mod" ||
|
|
136
|
-
filename === "go.sum" ||
|
|
137
|
-
filename === "void.json" ||
|
|
138
|
-
filename === "plugin.wasm") {
|
|
148
|
+
// Prevent copying build output directory or skipped directories
|
|
149
|
+
if (fullPath === buildOutputDir || fullPath.startsWith(buildOutputDir + path.sep)) {
|
|
139
150
|
continue;
|
|
140
151
|
}
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
if (entry.name === "@void" ||
|
|
153
|
+
entry.name === "@tgrv" ||
|
|
154
|
+
entry.name === "target" ||
|
|
155
|
+
entry.name === ".git" ||
|
|
156
|
+
entry.name === "node_modules" ||
|
|
157
|
+
entry.name === "void.json" ||
|
|
158
|
+
(wasmFilename && entry.name === wasmFilename)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (entry.isDirectory()) {
|
|
163
|
+
traverse(fullPath);
|
|
164
|
+
} else {
|
|
165
|
+
const relativePath = path.relative(src, fullPath);
|
|
166
|
+
|
|
167
|
+
// Check if relativePath matches any glob in filesConfig
|
|
168
|
+
const matches = filesConfig.some(pattern => matchGlob(relativePath, pattern));
|
|
169
|
+
if (matches) {
|
|
170
|
+
const destPath = path.join(dest, relativePath);
|
|
171
|
+
ensureDir(path.dirname(destPath));
|
|
172
|
+
fs.copyFileSync(fullPath, destPath);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
143
175
|
}
|
|
144
176
|
}
|
|
177
|
+
|
|
178
|
+
traverse(src);
|
|
145
179
|
}
|
|
146
180
|
|
|
147
181
|
const args = process.argv.slice(2);
|
|
@@ -192,10 +226,20 @@ function runBuild(pluginPathArg) {
|
|
|
192
226
|
process.exit(1);
|
|
193
227
|
}
|
|
194
228
|
|
|
229
|
+
if (manifest.types) {
|
|
230
|
+
const typesPath = path.resolve(absolutePluginDir, manifest.types);
|
|
231
|
+
if (!fs.existsSync(typesPath) || !fs.statSync(typesPath).isFile()) {
|
|
232
|
+
console.error(`${cross} ${colors.red}Error: Types file '${manifest.types}' specified in void.json does not exist.${colors.reset}`);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
195
237
|
const pluginName = manifest.name;
|
|
196
238
|
const pluginType = manifest.type;
|
|
197
239
|
const buildDir = manifest.buildDir;
|
|
198
240
|
const buildOutputDir = path.join(absolutePluginDir, buildDir);
|
|
241
|
+
const exportName = manifest.export || "plugin";
|
|
242
|
+
const wasmFilename = `${exportName}.wasm`;
|
|
199
243
|
|
|
200
244
|
if (pluginType === "sdk") {
|
|
201
245
|
console.log(`\n${info} Packaging SDK '${colors.bold}${pluginName}${colors.reset}' to local build at: ${colors.bold}${buildOutputDir}${colors.reset}`);
|
|
@@ -227,9 +271,9 @@ function runBuild(pluginPathArg) {
|
|
|
227
271
|
}
|
|
228
272
|
// Go plugin compilation
|
|
229
273
|
else if (pluginType === "go") {
|
|
230
|
-
const outputWasm = path.join(absolutePluginDir,
|
|
231
|
-
console.log(`${info} Running: ${colors.blue}go build -o
|
|
232
|
-
const compileSuccess = runCommand(
|
|
274
|
+
const outputWasm = path.join(absolutePluginDir, wasmFilename);
|
|
275
|
+
console.log(`${info} Running: ${colors.blue}go build -o ${wasmFilename}${colors.reset}`);
|
|
276
|
+
const compileSuccess = runCommand(`go build -o ${wasmFilename}`, {
|
|
233
277
|
cwd: absolutePluginDir,
|
|
234
278
|
env: { ...process.env, GOOS: "wasip1", GOARCH: "wasm" },
|
|
235
279
|
});
|
|
@@ -247,7 +291,7 @@ function runBuild(pluginPathArg) {
|
|
|
247
291
|
ensureDir(buildOutputDir);
|
|
248
292
|
|
|
249
293
|
// Copy WASM
|
|
250
|
-
fs.copyFileSync(builtWasmPath, path.join(buildOutputDir,
|
|
294
|
+
fs.copyFileSync(builtWasmPath, path.join(buildOutputDir, wasmFilename));
|
|
251
295
|
|
|
252
296
|
// Clean up temporary compiled WASM
|
|
253
297
|
if (path.dirname(builtWasmPath) === absolutePluginDir) {
|
|
@@ -258,7 +302,7 @@ function runBuild(pluginPathArg) {
|
|
|
258
302
|
}
|
|
259
303
|
}
|
|
260
304
|
|
|
261
|
-
// Write package.json if it does not already exist, or check version if it does
|
|
305
|
+
// Write package.json if it does not already exist, or check version/types if it does
|
|
262
306
|
const packageJsonPath = path.join(buildOutputDir, "package.json");
|
|
263
307
|
if (!fs.existsSync(packageJsonPath)) {
|
|
264
308
|
const pkgJson = {
|
|
@@ -273,20 +317,39 @@ function runBuild(pluginPathArg) {
|
|
|
273
317
|
access: "public"
|
|
274
318
|
}
|
|
275
319
|
};
|
|
320
|
+
if (manifest.types) {
|
|
321
|
+
pkgJson.types = manifest.types;
|
|
322
|
+
}
|
|
276
323
|
fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
277
324
|
} else {
|
|
278
325
|
try {
|
|
279
326
|
const existingPkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
327
|
+
let changed = false;
|
|
280
328
|
if (existingPkg.version !== manifest.version) {
|
|
281
329
|
console.log(`${info} Version mismatch detected in ${packageJsonPath}. Updating from ${existingPkg.version} to ${manifest.version} to match void.json.`);
|
|
282
330
|
existingPkg.version = manifest.version || "1.0.0";
|
|
331
|
+
changed = true;
|
|
332
|
+
}
|
|
333
|
+
if (manifest.types && existingPkg.types !== manifest.types) {
|
|
334
|
+
existingPkg.types = manifest.types;
|
|
335
|
+
changed = true;
|
|
336
|
+
}
|
|
337
|
+
if (changed) {
|
|
283
338
|
fs.writeFileSync(packageJsonPath, JSON.stringify(existingPkg, null, 2));
|
|
284
339
|
}
|
|
285
340
|
} catch (err) {
|
|
286
|
-
console.warn(`${warning} Failed to check/update existing package.json version: ${err.message}`);
|
|
341
|
+
console.warn(`${warning} Failed to check/update existing package.json version/types: ${err.message}`);
|
|
287
342
|
}
|
|
288
343
|
}
|
|
289
344
|
|
|
345
|
+
// Copy types file if specified
|
|
346
|
+
if (manifest.types) {
|
|
347
|
+
const typesPath = path.resolve(absolutePluginDir, manifest.types);
|
|
348
|
+
const destTypesPath = path.join(buildOutputDir, manifest.types);
|
|
349
|
+
ensureDir(path.dirname(destTypesPath));
|
|
350
|
+
fs.copyFileSync(typesPath, destTypesPath);
|
|
351
|
+
}
|
|
352
|
+
|
|
290
353
|
// Generate standard ESM index.js loader
|
|
291
354
|
const indexJsContent = `import { runtime } from "@tgrv/void-runtime";
|
|
292
355
|
import { fileURLToPath } from "url";
|
|
@@ -295,14 +358,14 @@ import { dirname, join } from "path";
|
|
|
295
358
|
const __filename = fileURLToPath(import.meta.url);
|
|
296
359
|
const __dirname = dirname(__filename);
|
|
297
360
|
|
|
298
|
-
const
|
|
299
|
-
join(__dirname, "
|
|
361
|
+
const ${exportName} = await runtime.load(
|
|
362
|
+
join(__dirname, "${wasmFilename}")
|
|
300
363
|
);
|
|
301
364
|
|
|
302
|
-
export default
|
|
365
|
+
export default ${exportName};
|
|
303
366
|
`;
|
|
304
367
|
fs.writeFileSync(path.join(buildOutputDir, "index.js"), indexJsContent);
|
|
305
|
-
copyNonSourceFiles(absolutePluginDir, buildOutputDir, buildOutputDir);
|
|
368
|
+
copyNonSourceFiles(absolutePluginDir, buildOutputDir, buildOutputDir, manifest.files, wasmFilename);
|
|
306
369
|
console.log(`${tick} ${colors.green}Successfully completed compilation & packaging!${colors.reset}`);
|
|
307
370
|
return buildOutputDir;
|
|
308
371
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Void Go Plugin Template
|
|
2
|
+
|
|
3
|
+
A template for building WebAssembly (WASM) plugins for the **Void** framework using Go.
|
|
4
|
+
|
|
5
|
+
## 🚀 Getting Started
|
|
6
|
+
|
|
7
|
+
### 1. Where to Start
|
|
8
|
+
Write your plugin logic inside [main.go](file:///main.go). Exported functions must match the signature:
|
|
9
|
+
```go
|
|
10
|
+
func MyFunction(args map[string]json.RawMessage) (any, error)
|
|
11
|
+
```
|
|
12
|
+
And register them in `main()` using the Void SDK:
|
|
13
|
+
```go
|
|
14
|
+
func main() {
|
|
15
|
+
void.Register("my_function", MyFunction)
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. Build the Plugin
|
|
20
|
+
Compile your plugin by running the build command in this directory:
|
|
21
|
+
```bash
|
|
22
|
+
npx void build
|
|
23
|
+
```
|
|
24
|
+
This compiles the Go code into a WebAssembly binary (`.wasm`) and packages it inside the build output directory configured in `void.json`.
|
|
25
|
+
|
|
26
|
+
### 3. Test/Install Locally
|
|
27
|
+
To test the built plugin in a local Void application:
|
|
28
|
+
1. Initialize your application project: `npx void init`
|
|
29
|
+
2. Add your compiled local plugin build output folder path:
|
|
30
|
+
```bash
|
|
31
|
+
npx void add ./path/to/plugin/@void/<plugin-name>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 4. Publish
|
|
35
|
+
To build and publish the plugin to the npm registry:
|
|
36
|
+
```bash
|
|
37
|
+
npx void publish
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## ⚙️ Configuration (`void.json`)
|
|
43
|
+
|
|
44
|
+
Your plugin configuration is defined in `void.json`. Here are the available fields:
|
|
45
|
+
|
|
46
|
+
- **`name`**: The package name of your plugin (e.g. `@void/my-plugin`).
|
|
47
|
+
- **`version`**: The current semantic version of the plugin.
|
|
48
|
+
- **`type`**: The compilation target language. Set to `"go"`.
|
|
49
|
+
- **`buildDir`**: The target directory where compilation and wrapping assets are generated (e.g. `@void/my-plugin`).
|
|
50
|
+
- **`types`**: Path to the TypeScript declaration file (e.g. `"types.d.ts"`). During the build process, the CLI validates its existence, copies it to the build output, and automatically sets the `"types"` entry in `package.json` for autocomplete and editor support.
|
|
51
|
+
- **`export`**: The name of the JavaScript variable used to load the WASM binary, which is also the default export name (e.g. `"myPlugin"`).
|
|
52
|
+
- **`files`**: An array of glob patterns of non-source files (such as `*.md` or `*.d.ts`) to copy from the plugin root into the build output folder.
|
package/templates/go/void.json
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Void Rust Plugin Template
|
|
2
|
+
|
|
3
|
+
A template for building WebAssembly (WASM) plugins for the **Void** framework using Rust.
|
|
4
|
+
|
|
5
|
+
## 🚀 Getting Started
|
|
6
|
+
|
|
7
|
+
### 1. Where to Start
|
|
8
|
+
Write your plugin logic inside [src/lib.rs](file:///src/lib.rs). Functions must be annotated with the Void SDK macro and registered:
|
|
9
|
+
Refer to the Void Rust SDK documentation for macro registry instructions.
|
|
10
|
+
|
|
11
|
+
### 2. Build the Plugin
|
|
12
|
+
Compile your plugin by running the build command in this directory:
|
|
13
|
+
```bash
|
|
14
|
+
npx void build
|
|
15
|
+
```
|
|
16
|
+
This compiles the Rust source code into a WebAssembly binary (`.wasm`) using `cargo build --target wasm32-unknown-unknown --release` and packages it under the build directory configured in `void.json`.
|
|
17
|
+
|
|
18
|
+
### 3. Test/Install Locally
|
|
19
|
+
To test the built plugin in a local Void application:
|
|
20
|
+
1. Initialize your application project: `npx void init`
|
|
21
|
+
2. Add your compiled local plugin build output folder path:
|
|
22
|
+
```bash
|
|
23
|
+
npx void add ./path/to/plugin/@void/<plugin-name>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 4. Publish
|
|
27
|
+
To build and publish the plugin to the npm registry:
|
|
28
|
+
```bash
|
|
29
|
+
npx void publish
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## ⚙️ Configuration (`void.json`)
|
|
35
|
+
|
|
36
|
+
Your plugin configuration is defined in `void.json`. Here are the available fields:
|
|
37
|
+
|
|
38
|
+
- **`name`**: The package name of your plugin (e.g. `@void/my-plugin`).
|
|
39
|
+
- **`version`**: The current semantic version of the plugin.
|
|
40
|
+
- **`type`**: The compilation target language. Set to `"rust"`.
|
|
41
|
+
- **`buildDir`**: The target directory where compilation and wrapping assets are generated (e.g. `@void/my-plugin`).
|
|
42
|
+
- **`types`**: Path to the TypeScript declaration file (e.g. `"types.d.ts"`). During the build process, the CLI validates its existence, copies it to the build output, and automatically sets the `"types"` entry in `package.json` for autocomplete and editor support.
|
|
43
|
+
- **`export`**: The name of the JavaScript variable used to load the WASM binary, which is also the default export name (e.g. `"myPlugin"`).
|
|
44
|
+
- **`files`**: An array of glob patterns of non-source files (such as `*.md` or `*.d.ts`) to copy from the plugin root into the build output folder.
|
package/templates/rust/void.json
CHANGED