@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 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,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, "plugin.wasm");
187
- console.log(`${info} Running: ${colors.blue}go build -o plugin.wasm${colors.reset}`);
188
- 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}`, {
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, "plugin.wasm"));
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 plugin = await runtime.load(
255
- join(__dirname, "plugin.wasm")
361
+ const ${exportName} = await runtime.load(
362
+ join(__dirname, "${wasmFilename}")
256
363
  );
257
364
 
258
- export default plugin;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tgrv/void-cli",
3
- "version": "1.0.8",
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
  }