@tgrv/void-cli 1.0.8 → 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 +117 -9
- 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,6 +100,84 @@ function copyFolderRecursive(src, dest, replacements = {}) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
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;
|
|
114
|
+
}
|
|
115
|
+
const prefix = normalizedPattern.endsWith('/') ? normalizedPattern : normalizedPattern + '/';
|
|
116
|
+
if (normalizedPath.startsWith(prefix)) {
|
|
117
|
+
return true;
|
|
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));
|
|
132
|
+
|
|
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);
|
|
147
|
+
|
|
148
|
+
// Prevent copying build output directory or skipped directories
|
|
149
|
+
if (fullPath === buildOutputDir || fullPath.startsWith(buildOutputDir + path.sep)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
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
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
traverse(src);
|
|
179
|
+
}
|
|
180
|
+
|
|
103
181
|
const args = process.argv.slice(2);
|
|
104
182
|
const command = args[0];
|
|
105
183
|
|
|
@@ -148,10 +226,20 @@ function runBuild(pluginPathArg) {
|
|
|
148
226
|
process.exit(1);
|
|
149
227
|
}
|
|
150
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
|
+
|
|
151
237
|
const pluginName = manifest.name;
|
|
152
238
|
const pluginType = manifest.type;
|
|
153
239
|
const buildDir = manifest.buildDir;
|
|
154
240
|
const buildOutputDir = path.join(absolutePluginDir, buildDir);
|
|
241
|
+
const exportName = manifest.export || "plugin";
|
|
242
|
+
const wasmFilename = `${exportName}.wasm`;
|
|
155
243
|
|
|
156
244
|
if (pluginType === "sdk") {
|
|
157
245
|
console.log(`\n${info} Packaging SDK '${colors.bold}${pluginName}${colors.reset}' to local build at: ${colors.bold}${buildOutputDir}${colors.reset}`);
|
|
@@ -183,9 +271,9 @@ function runBuild(pluginPathArg) {
|
|
|
183
271
|
}
|
|
184
272
|
// Go plugin compilation
|
|
185
273
|
else if (pluginType === "go") {
|
|
186
|
-
const outputWasm = path.join(absolutePluginDir,
|
|
187
|
-
console.log(`${info} Running: ${colors.blue}go build -o
|
|
188
|
-
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}`, {
|
|
189
277
|
cwd: absolutePluginDir,
|
|
190
278
|
env: { ...process.env, GOOS: "wasip1", GOARCH: "wasm" },
|
|
191
279
|
});
|
|
@@ -203,7 +291,7 @@ function runBuild(pluginPathArg) {
|
|
|
203
291
|
ensureDir(buildOutputDir);
|
|
204
292
|
|
|
205
293
|
// Copy WASM
|
|
206
|
-
fs.copyFileSync(builtWasmPath, path.join(buildOutputDir,
|
|
294
|
+
fs.copyFileSync(builtWasmPath, path.join(buildOutputDir, wasmFilename));
|
|
207
295
|
|
|
208
296
|
// Clean up temporary compiled WASM
|
|
209
297
|
if (path.dirname(builtWasmPath) === absolutePluginDir) {
|
|
@@ -214,7 +302,7 @@ function runBuild(pluginPathArg) {
|
|
|
214
302
|
}
|
|
215
303
|
}
|
|
216
304
|
|
|
217
|
-
// 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
|
|
218
306
|
const packageJsonPath = path.join(buildOutputDir, "package.json");
|
|
219
307
|
if (!fs.existsSync(packageJsonPath)) {
|
|
220
308
|
const pkgJson = {
|
|
@@ -229,20 +317,39 @@ function runBuild(pluginPathArg) {
|
|
|
229
317
|
access: "public"
|
|
230
318
|
}
|
|
231
319
|
};
|
|
320
|
+
if (manifest.types) {
|
|
321
|
+
pkgJson.types = manifest.types;
|
|
322
|
+
}
|
|
232
323
|
fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
233
324
|
} else {
|
|
234
325
|
try {
|
|
235
326
|
const existingPkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
327
|
+
let changed = false;
|
|
236
328
|
if (existingPkg.version !== manifest.version) {
|
|
237
329
|
console.log(`${info} Version mismatch detected in ${packageJsonPath}. Updating from ${existingPkg.version} to ${manifest.version} to match void.json.`);
|
|
238
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) {
|
|
239
338
|
fs.writeFileSync(packageJsonPath, JSON.stringify(existingPkg, null, 2));
|
|
240
339
|
}
|
|
241
340
|
} catch (err) {
|
|
242
|
-
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}`);
|
|
243
342
|
}
|
|
244
343
|
}
|
|
245
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
|
+
|
|
246
353
|
// Generate standard ESM index.js loader
|
|
247
354
|
const indexJsContent = `import { runtime } from "@tgrv/void-runtime";
|
|
248
355
|
import { fileURLToPath } from "url";
|
|
@@ -251,13 +358,14 @@ import { dirname, join } from "path";
|
|
|
251
358
|
const __filename = fileURLToPath(import.meta.url);
|
|
252
359
|
const __dirname = dirname(__filename);
|
|
253
360
|
|
|
254
|
-
const
|
|
255
|
-
join(__dirname, "
|
|
361
|
+
const ${exportName} = await runtime.load(
|
|
362
|
+
join(__dirname, "${wasmFilename}")
|
|
256
363
|
);
|
|
257
364
|
|
|
258
|
-
export default
|
|
365
|
+
export default ${exportName};
|
|
259
366
|
`;
|
|
260
367
|
fs.writeFileSync(path.join(buildOutputDir, "index.js"), indexJsContent);
|
|
368
|
+
copyNonSourceFiles(absolutePluginDir, buildOutputDir, buildOutputDir, manifest.files, wasmFilename);
|
|
261
369
|
console.log(`${tick} ${colors.green}Successfully completed compilation & packaging!${colors.reset}`);
|
|
262
370
|
return buildOutputDir;
|
|
263
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