@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 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 WASM binary under a local scoped output folder configured in `void.json`. Defaults to current folder (`.`).
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 triggers `npm publish` natively from inside the scoped build directory. Defaults to current folder (`.`).
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
- Downloads the plugin from the NPM registry, or links local build outputs during development, adding dependencies to `void.config.json` and `package.json`.
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
- ensureDir(dest);
105
- const entries = fs.readdirSync(src, { withFileTypes: true });
106
- for (const entry of entries) {
107
- const srcPath = path.join(src, entry.name);
108
- const destPath = path.join(dest, entry.name);
109
-
110
- // Prevent copying the build output directory into itself
111
- if (srcPath === buildOutputDir || srcPath.startsWith(buildOutputDir + path.sep)) {
112
- continue;
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
- // Skip standard build/dev folders
116
- if (entry.name === "@void" ||
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 (entry.isDirectory()) {
125
- copyNonSourceFiles(srcPath, destPath, buildOutputDir);
126
- } else {
127
- const ext = path.extname(entry.name).toLowerCase();
128
- const filename = entry.name.toLowerCase();
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
- // Exclude Go/Rust/Cargo files and build binaries/configs
131
- if (ext === ".go" ||
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
- fs.copyFileSync(srcPath, destPath);
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, "plugin.wasm");
231
- console.log(`${info} Running: ${colors.blue}go build -o plugin.wasm${colors.reset}`);
232
- const compileSuccess = runCommand("go build -o plugin.wasm", {
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, "plugin.wasm"));
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 plugin = await runtime.load(
299
- join(__dirname, "plugin.wasm")
361
+ const ${exportName} = await runtime.load(
362
+ join(__dirname, "${wasmFilename}")
300
363
  );
301
364
 
302
- export default plugin;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tgrv/void-cli",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "void": "./bin/void.js"
@@ -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.
@@ -1,6 +1,11 @@
1
1
  {
2
- "name": "@void/{{name}}",
2
+ "name": "{{name}}",
3
3
  "version": "1.0.0",
4
4
  "type": "go",
5
- "buildDir": "@void/{{name}}"
5
+ "buildDir": "@void/{{name}}",
6
+ "export": "{{name}}",
7
+ "files": [
8
+ "*.d.ts",
9
+ "*.md"
10
+ ]
6
11
  }
@@ -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.
@@ -1,6 +1,11 @@
1
1
  {
2
- "name": "@void/{{name}}",
2
+ "name": "{{name}}",
3
3
  "version": "1.0.0",
4
4
  "type": "rust",
5
- "buildDir": "@void/{{name}}"
5
+ "buildDir": "@void/{{name}}",
6
+ "export": "{{name}}",
7
+ "files": [
8
+ "*.d.ts",
9
+ "*.md"
10
+ ]
6
11
  }