@gasm-compiler/cli 0.1.1 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/cli.js +123 -16
  3. package/package.json +2 -4
package/README.md CHANGED
@@ -4,6 +4,9 @@
4
4
 
5
5
  Compile WebAssembly (`.wasm`) or AssemblyScript (`.as`/`.ts`) files to WGSL compute shaders from the command line. Built on top of [`@gasm-compiler/core`](https://www.npmjs.com/package/@gasm-compiler/core).
6
6
 
7
+ Version 0.2 defaults to strict Gasm v0.2 compilation. Use
8
+ `--spec-version 0.1` only for archived v0.1 inputs.
9
+
7
10
  ---
8
11
 
9
12
  ## Installation
package/dist/cli.js CHANGED
@@ -9,10 +9,13 @@ import { parseArgs } from "jsr:@std/cli/parse-args";
9
9
  import { resolve } from "jsr:@std/path";
10
10
  import { ensureFile } from "jsr:@std/fs";
11
11
  import {
12
+ compileToArtifact,
12
13
  compileWithDiagnostics,
13
14
  disassembleToWAT,
15
+ isParseError,
14
16
  parseWasmModule,
15
- isParseError
17
+ prepareModule,
18
+ validateGasmMetadataSchema
16
19
  } from "@gasm-compiler/core";
17
20
  function printDiagnostics(diagnostics, verbose) {
18
21
  for (const w of diagnostics.warnings) {
@@ -24,13 +27,19 @@ function printDiagnostics(diagnostics, verbose) {
24
27
  for (const d of diagnostics.demotions) {
25
28
  counts.set(d.kind, (counts.get(d.kind) ?? 0) + 1);
26
29
  }
27
- const parts = [...counts.entries()].map(([kind, count]) => `${kind} (${count})`);
30
+ const parts = [...counts.entries()].map(
31
+ ([kind, count]) => `${kind} (${count})`
32
+ );
28
33
  console.error(`demotions: ${parts.join(", ")}`);
29
34
  }
30
35
  }
31
36
  async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
32
37
  const {
33
38
  output: outputFile,
39
+ metadata: metadataFile,
40
+ prepare,
41
+ strict = false,
42
+ specVersion: requestedSpecVersion,
34
43
  watOutput: watOutputFile,
35
44
  verbose = false,
36
45
  keepWasm = false,
@@ -41,6 +50,25 @@ async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
41
50
  sourceMapping
42
51
  } = options;
43
52
  try {
53
+ if (requestedSpecVersion !== void 0 && requestedSpecVersion !== "0.1" && requestedSpecVersion !== "0.2") {
54
+ throw new Error(
55
+ `Unsupported spec version "${requestedSpecVersion}"; expected 0.1 or 0.2`
56
+ );
57
+ }
58
+ if (prepare !== void 0 && prepare !== "auto") {
59
+ throw new Error(
60
+ `Unsupported prepare mode "${prepare}"; expected "auto"`
61
+ );
62
+ }
63
+ if (prepare && strict) {
64
+ throw new Error("--prepare and --strict cannot be used together");
65
+ }
66
+ const specVersion = requestedSpecVersion ?? "0.2";
67
+ if (metadataFile && specVersion !== "0.2") {
68
+ throw new Error(
69
+ "--metadata requires --spec-version 0.2"
70
+ );
71
+ }
44
72
  if (verbose) console.log(`[1/4] Validating input file: ${inputFile}`);
45
73
  const resolvedInput = resolve(inputFile);
46
74
  const stats = await Deno.stat(resolvedInput);
@@ -49,7 +77,9 @@ async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
49
77
  }
50
78
  const isWasm = resolvedInput.endsWith(".wasm");
51
79
  if (!isWasm && !resolvedInput.endsWith(".as") && !resolvedInput.endsWith(".ts")) {
52
- console.warn("Warning: Input file does not have .as, .ts, or .wasm extension");
80
+ console.warn(
81
+ "Warning: Input file does not have .as, .ts, or .wasm extension"
82
+ );
53
83
  }
54
84
  let wasmPath = resolvedInput.replace(/\.(as|ts)$/, ".wasm");
55
85
  if (!isWasm) {
@@ -104,31 +134,82 @@ ${errorMsg}`);
104
134
  const demotionPolicy = {};
105
135
  if (denyF64Demotion) demotionPolicy.f64 = "deny";
106
136
  if (allowI64Demotion) demotionPolicy.i64 = "allow-lossy";
107
- const validLevels = ["none", "minimal", "normal", "detailed", "verbose"];
137
+ const validLevels = [
138
+ "none",
139
+ "minimal",
140
+ "normal",
141
+ "detailed",
142
+ "verbose"
143
+ ];
108
144
  let sourceMappingLevel;
109
145
  if (sourceMapping) {
110
146
  if (validLevels.includes(sourceMapping)) {
111
147
  sourceMappingLevel = sourceMapping;
112
148
  } else {
113
- console.warn(`Warning: Unknown source-mapping level "${sourceMapping}", using "normal"`);
149
+ console.warn(
150
+ `Warning: Unknown source-mapping level "${sourceMapping}", using "normal"`
151
+ );
114
152
  sourceMappingLevel = "normal";
115
153
  }
116
154
  }
117
- const result = compileWithDiagnostics(wasmBytes, {
155
+ let compileOptions = {
118
156
  optimize,
119
157
  demotionPolicy,
120
158
  warningsAsErrors,
121
- sourceMapping: sourceMappingLevel
122
- });
159
+ sourceMapping: sourceMappingLevel,
160
+ specVersion
161
+ };
162
+ let compilationBytes = wasmBytes;
163
+ if (prepare === "auto" || strict) {
164
+ const prepared = prepareModule(wasmBytes, {
165
+ ...compileOptions,
166
+ mode: strict ? "strict" : "integrator"
167
+ });
168
+ for (const advisory of prepared.advisories) {
169
+ console.error(`advisory: ${advisory.message}`);
170
+ }
171
+ if (prepared.errors.length > 0) {
172
+ const first = prepared.errors[0];
173
+ throw new Error(`${first.code}: ${first.message}`);
174
+ }
175
+ compilationBytes = prepared.wasmBytes;
176
+ compileOptions = prepared.compileOptions;
177
+ }
178
+ const result = metadataFile ? compileToArtifact(compilationBytes, compileOptions) : compileWithDiagnostics(compilationBytes, compileOptions);
123
179
  printDiagnostics(result.diagnostics, verbose);
124
180
  if (!result.ok) {
125
181
  const first = result.diagnostics.errors[0];
126
- throw new Error(first?.message ?? "Compilation failed");
182
+ throw new Error(
183
+ first ? `${first.code}: ${first.message}` : "Compilation failed"
184
+ );
127
185
  }
128
186
  const wgslCode = result.wgsl;
187
+ if (metadataFile) {
188
+ if (!("metadata" in result)) {
189
+ throw new Error("Compiler did not produce metadata");
190
+ }
191
+ const schema = validateGasmMetadataSchema(result.metadata);
192
+ if (!schema.valid) {
193
+ throw new Error(
194
+ `Generated metadata failed schema validation: ${schema.errors.join(", ")}`
195
+ );
196
+ }
197
+ const resolvedMetadata = resolve(metadataFile);
198
+ await ensureFile(resolvedMetadata);
199
+ await Deno.writeTextFile(
200
+ resolvedMetadata,
201
+ `${JSON.stringify(result.metadata, null, 2)}
202
+ `
203
+ );
204
+ if (verbose) {
205
+ console.log(`\u2713 Metadata written to: ${resolvedMetadata}`);
206
+ } else {
207
+ console.log(`\u2713 Metadata written to: ${metadataFile}`);
208
+ }
209
+ }
129
210
  if (watOutputFile) {
130
211
  if (verbose) console.log("[4a/4] Generating WAT disassembly...");
131
- const parseResult = parseWasmModule(wasmBytes);
212
+ const parseResult = parseWasmModule(compilationBytes);
132
213
  if (!isParseError(parseResult)) {
133
214
  const watCode = disassembleToWAT(parseResult);
134
215
  const resolvedWatOutput = resolve(watOutputFile);
@@ -140,7 +221,9 @@ ${errorMsg}`);
140
221
  console.log(`\u2713 WAT written to: ${watOutputFile}`);
141
222
  }
142
223
  } else {
143
- console.warn(`Warning: Failed to parse WASM for WAT disassembly: ${parseResult.message}`);
224
+ console.warn(
225
+ `Warning: Failed to parse WASM for WAT disassembly: ${parseResult.message}`
226
+ );
144
227
  }
145
228
  }
146
229
  if (outputFile) {
@@ -174,7 +257,10 @@ async function getAssemblyScriptPath() {
174
257
  const possiblePaths = [
175
258
  // Relative to CLI package
176
259
  resolve(scriptDir, "../../node_modules/.bin/asc"),
177
- resolve(scriptDir, "../../node_modules/.pnpm/assemblyscript@0.28.9/node_modules/assemblyscript/node_modules/.bin/asc"),
260
+ resolve(
261
+ scriptDir,
262
+ "../../node_modules/.pnpm/assemblyscript@0.28.9/node_modules/assemblyscript/node_modules/.bin/asc"
263
+ ),
178
264
  resolve(scriptDir, "../node_modules/.bin/asc"),
179
265
  resolve(scriptDir, "../examples/node_modules/.bin/asc"),
180
266
  // Relative to workspace root
@@ -196,16 +282,24 @@ async function getAssemblyScriptPath() {
196
282
  }
197
283
 
198
284
  // src/cli.ts
199
- var VERSION = "0.1.0";
285
+ var VERSION = "0.3.0";
200
286
  async function main() {
201
287
  const args = parseArgs(Deno.args, {
202
- string: ["output", "wat-output", "source-mapping"],
288
+ string: [
289
+ "output",
290
+ "metadata",
291
+ "prepare",
292
+ "spec-version",
293
+ "wat-output",
294
+ "source-mapping"
295
+ ],
203
296
  boolean: [
204
297
  "help",
205
298
  "version",
206
299
  "verbose",
207
300
  "keep-wasm",
208
301
  "optimize",
302
+ "strict",
209
303
  "deny-f64-demotion",
210
304
  "allow-i64-demotion",
211
305
  "warnings-as-errors"
@@ -229,12 +323,18 @@ async function main() {
229
323
  if (command === "compile") {
230
324
  if (args._.length < 2) {
231
325
  console.error("Error: compile requires an input file");
232
- console.error("Usage: cli compile <input.as|wasm> [--output <output.wgsl>]");
326
+ console.error(
327
+ "Usage: cli compile <input.as|wasm> [--output <output.wgsl>]"
328
+ );
233
329
  Deno.exit(1);
234
330
  }
235
331
  const inputFile = args._[1];
236
332
  const options = {
237
333
  output: args.output,
334
+ metadata: args.metadata,
335
+ prepare: args.prepare,
336
+ strict: args.strict,
337
+ specVersion: args["spec-version"],
238
338
  watOutput: args["wat-output"],
239
339
  verbose: args.verbose,
240
340
  keepWasm: args["keep-wasm"],
@@ -251,7 +351,10 @@ async function main() {
251
351
  Deno.exit(1);
252
352
  }
253
353
  } catch (error) {
254
- console.error("Error:", error instanceof Error ? error.message : String(error));
354
+ console.error(
355
+ "Error:",
356
+ error instanceof Error ? error.message : String(error)
357
+ );
255
358
  if (args.verbose && error instanceof Error && error.stack) {
256
359
  console.error("\nStack trace:");
257
360
  console.error(error.stack);
@@ -274,6 +377,10 @@ COMMANDS:
274
377
 
275
378
  OPTIONS:
276
379
  --output <file> Write WGSL output to file (default: stdout)
380
+ --metadata <file> Write validated Gasm v0.2 metadata sidecar JSON
381
+ --prepare auto Infer and inject extensions for transient builds
382
+ --strict Analyze without modifying the input Wasm
383
+ --spec-version <version> Compile as Gasm 0.1 or 0.2 (default: 0.2)
277
384
  --wat-output <file> Also write WAT (WebAssembly Text) disassembly to file
278
385
  --verbose Show detailed compilation steps
279
386
  --keep-wasm Keep intermediate WASM files
package/package.json CHANGED
@@ -1,16 +1,14 @@
1
1
  {
2
2
  "name": "@gasm-compiler/cli",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for compiling WebAssembly/AssemblyScript to WGSL using Gasm Compiler",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
7
- "types": "./dist/cli.d.ts",
8
7
  "bin": {
9
8
  "gasm-compiler": "./dist/cli.js"
10
9
  },
11
10
  "exports": {
12
11
  ".": {
13
- "types": "./dist/cli.d.ts",
14
12
  "import": "./dist/cli.js",
15
13
  "default": "./dist/cli.js"
16
14
  }
@@ -35,7 +33,7 @@
35
33
  "cli"
36
34
  ],
37
35
  "dependencies": {
38
- "@gasm-compiler/core": "0.1.1"
36
+ "@gasm-compiler/core": "0.5.0"
39
37
  },
40
38
  "devDependencies": {
41
39
  "assemblyscript": "v0.28.9",