@bytecodealliance/jco 1.13.2 → 1.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytecodealliance/jco",
3
- "version": "1.13.2",
3
+ "version": "1.14.0",
4
4
  "description": "JavaScript tooling for working with WebAssembly Components",
5
5
  "homepage": "https://github.com/bytecodealliance/jco#readme",
6
6
  "author": "Guy Bedford",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@bytecodealliance/componentize-js": "^0.18.4",
70
- "@bytecodealliance/preview2-shim": "^0.17.2",
70
+ "@bytecodealliance/preview2-shim": "^0.17.3",
71
71
  "binaryen": "^123.0.0",
72
72
  "chalk-template": "^1",
73
73
  "commander": "^14",
@@ -77,9 +77,9 @@
77
77
  },
78
78
  "devDependencies": {
79
79
  "@commitlint/config-conventional": "^19.8.1",
80
- "@types/node": "^24.2.1",
81
- "@typescript-eslint/eslint-plugin": "^8.39.0",
82
- "@typescript-eslint/parser": "^8.39.0",
80
+ "@types/node": "^24.3.0",
81
+ "@typescript-eslint/eslint-plugin": "^8.39.1",
82
+ "@typescript-eslint/parser": "^8.39.1",
83
83
  "commitlint": "^19.8.1",
84
84
  "conventional-changelog-conventionalcommits": "^9.1.0",
85
85
  "eslint": "^9.33.0",
@@ -87,7 +87,7 @@
87
87
  "eslint-plugin-prettier": "^5.5.4",
88
88
  "mime": "^4.0.7",
89
89
  "prettier": "^3.6.2",
90
- "puppeteer": "^24.16.1",
90
+ "puppeteer": "^24.16.2",
91
91
  "semver": "^7.7.1",
92
92
  "smol-toml": "^1.4.2",
93
93
  "typescript": "^5.9.2",
@@ -28,6 +28,7 @@ export async function componentize(jsSource, opts) {
28
28
  enableFeatures: opts.enable,
29
29
  preview2Adapter: opts.preview2Adapter,
30
30
  debugBuild: opts.debugStarlingmonkeyBuild,
31
+ engine: opts.engine,
31
32
  debug: {
32
33
  bindings: opts.debugBindings,
33
34
  bindingsDir: opts.debugBindingsDir,
package/src/cmd/opt.js CHANGED
@@ -136,7 +136,8 @@ export async function optimizeComponent(componentBytes, opts) {
136
136
  metadata.range[0],
137
137
  metadata.range[1]
138
138
  ),
139
- args
139
+ args,
140
+ opts
140
141
  );
141
142
 
142
143
  // compute the size change, including the change to
@@ -280,13 +281,13 @@ export async function optimizeComponent(componentBytes, opts) {
280
281
  * @param {Array<string>} args
281
282
  * @returns {Promise<Uint8Array>}
282
283
  */
283
- async function wasmOpt(source, args) {
284
- const wasmOptPath = fileURLToPath(
285
- import.meta.resolve('binaryen/bin/wasm-opt')
286
- );
284
+ async function wasmOpt(source, args, transpileOpts) {
285
+ const wasmOptBin =
286
+ transpileOpts?.wasmOptBin ??
287
+ fileURLToPath(import.meta.resolve('binaryen/bin/wasm-opt'));
287
288
 
288
289
  try {
289
- return await spawnIOTmp(wasmOptPath, source, [...args, '-o']);
290
+ return await spawnIOTmp(wasmOptBin, source, [...args, '-o']);
290
291
  } catch (e) {
291
292
  if (e.toString().includes('BasicBlock requested')) {
292
293
  return wasmOpt(source, args);
@@ -1,6 +1,5 @@
1
1
  /* global Buffer */
2
2
 
3
- import { platform } from 'node:process';
4
3
  import { extname, basename, resolve } from 'node:path';
5
4
 
6
5
  import c from 'chalk-template';
@@ -18,13 +17,14 @@ import {
18
17
  writeFiles,
19
18
  ASYNC_WASI_IMPORTS,
20
19
  ASYNC_WASI_EXPORTS,
20
+ DEFAULT_ASYNC_MODE,
21
21
  } from '../common.js';
22
22
  import { $init as wasmToolsInit, tools } from '../../obj/wasm-tools.js';
23
23
  const { componentEmbed, componentNew } = tools;
24
24
 
25
25
  import ora from '#ora';
26
26
 
27
- const isWindows = platform === 'win32';
27
+ import { isWindows } from '../common.js';
28
28
 
29
29
  // These re-exports exist to avoid breaking backwards compatibility
30
30
  export { types, guestTypes, typesComponent } from './types.js';
@@ -122,6 +122,7 @@ async function wasm2Js(source) {
122
122
  * multiMemory?: bool,
123
123
  * experimentalIdlImports?: bool,
124
124
  * optArgs?: string[],
125
+ * wasmOptBin?: string[],
125
126
  * }} opts
126
127
  * @returns {Promise<{ files: { [filename: string]: Uint8Array }, imports: string[], exports: [string, 'function' | 'instance'][] }>}
127
128
  */
@@ -168,22 +169,36 @@ export async function transpileComponent(component, opts = {}) {
168
169
  instantiation = { tag: 'async' };
169
170
  }
170
171
 
171
- const asyncMode =
172
- !opts.asyncMode || opts.asyncMode === 'sync'
173
- ? null
174
- : {
175
- tag: opts.asyncMode,
176
- val: {
177
- imports: opts.asyncImports || [],
178
- exports: opts.asyncExports || [],
179
- },
180
- };
172
+ // Get the configured async mode then transform it into what the types component expects
173
+ // Build list of async imports/exports
174
+ let asyncImports = new Set([...(opts.asyncImports ?? [])]);
175
+ let asyncExports = new Set([...(opts.asyncExports ?? [])]);
176
+ let asyncMode = opts.asyncMode ?? DEFAULT_ASYNC_MODE;
177
+ if (asyncMode === 'sync' && asyncExports.size > 0) {
178
+ throw new Error(
179
+ 'async exports cannot be specified in sync mode (consider removing --async-mode=jspi)'
180
+ );
181
+ }
182
+ let asyncModeObj;
183
+ if (asyncMode === 'sync') {
184
+ asyncModeObj = null;
185
+ } else if (asyncMode === 'jspi') {
186
+ asyncModeObj = {
187
+ tag: 'jspi',
188
+ val: {
189
+ imports: [...asyncImports],
190
+ exports: [...asyncExports],
191
+ },
192
+ };
193
+ } else {
194
+ throw new Error(`invalid/unrecognized async mode [${asyncMode}]`);
195
+ }
181
196
 
182
197
  let { files, imports, exports } = generate(component, {
183
198
  name: opts.name ?? 'component',
184
199
  map: Object.entries(opts.map ?? {}),
185
200
  instantiation,
186
- asyncMode,
201
+ asyncMode: asyncModeObj,
187
202
  importBindings: opts.importBindings
188
203
  ? { tag: opts.importBindings }
189
204
  : null,
package/src/cmd/types.js CHANGED
@@ -1,4 +1,3 @@
1
- import { platform } from 'node:process';
2
1
  import { stat } from 'node:fs/promises';
3
2
  import { extname, basename, resolve } from 'node:path';
4
3
 
@@ -9,13 +8,13 @@ import {
9
8
  generateTypes,
10
9
  } from '../../obj/js-component-bindgen-component.js';
11
10
  import {
11
+ isWindows,
12
12
  writeFiles,
13
13
  ASYNC_WASI_IMPORTS,
14
14
  ASYNC_WASI_EXPORTS,
15
+ DEFAULT_ASYNC_MODE,
15
16
  } from '../common.js';
16
17
 
17
- const isWindows = platform === 'win32';
18
-
19
18
  export async function types(witPath, opts) {
20
19
  const files = await typesComponent(witPath, opts);
21
20
  await writeFiles(files, opts.quiet ? false : 'Generated Type Files');
@@ -47,6 +46,8 @@ export async function guestTypes(witPath, opts) {
47
46
  * features?: string[] | 'all',
48
47
  * asyncWasiImports?: string[],
49
48
  * asyncWasiExports?: string[],
49
+ * asyncExports?: string[],
50
+ * asyncImports?: string[],
50
51
  * guest?: bool,
51
52
  * }} opts
52
53
  * @returns {Promise<{ [filename: string]: Uint8Array }>}
@@ -67,6 +68,7 @@ export async function typesComponent(witPath, opts) {
67
68
  outDir += '/';
68
69
  }
69
70
 
71
+ // Bulid list of enabled features
70
72
  let features = null;
71
73
  if (opts.allFeatures) {
72
74
  features = { tag: 'all' };
@@ -76,24 +78,36 @@ export async function typesComponent(witPath, opts) {
76
78
  features = { tag: 'list', val: opts.features };
77
79
  }
78
80
 
81
+ // Build list of async imports/exports
82
+ let asyncImports = new Set([...(opts.asyncImports ?? [])]);
79
83
  if (opts.asyncWasiImports) {
80
- opts.asyncImports = ASYNC_WASI_IMPORTS.concat(opts.asyncImports || []);
84
+ ASYNC_WASI_IMPORTS.forEach((v) => asyncImports.add(v));
81
85
  }
86
+ let asyncExports = new Set([...(opts.asyncExports ?? [])]);
82
87
  if (opts.asyncWasiExports) {
83
- opts.asyncExports = ASYNC_WASI_EXPORTS.concat(opts.asyncExports || []);
88
+ ASYNC_WASI_EXPORTS.forEach((v) => asyncExports.add(v));
84
89
  }
85
90
 
86
- const asyncMode =
87
- !opts.asyncMode || opts.asyncMode === 'sync'
88
- ? null
89
- : {
90
- tag: opts.asyncMode,
91
- val: {
92
- imports: opts.asyncImports || [],
93
- exports: opts.asyncExports || [],
94
- },
95
- };
91
+ // For simple type generation, we choose the "async mode" for the user
92
+ // even though it is not relevant here (JSPI may not be used, as types may
93
+ // be used to generate a guest that is never transpiled).
94
+ let asyncMode = opts.asyncMode ?? DEFAULT_ASYNC_MODE;
95
+ let asyncModeObj;
96
+ if (asyncMode === 'jspi' || asyncExports.size > 0) {
97
+ asyncModeObj = {
98
+ tag: 'jspi',
99
+ val: {
100
+ imports: [...asyncImports],
101
+ exports: [...asyncExports],
102
+ },
103
+ };
104
+ } else if (asyncMode === 'sync') {
105
+ asyncModeObj = null;
106
+ } else {
107
+ throw new Error(`invalid/unrecognized async mode [${asyncMode}]`);
108
+ }
96
109
 
110
+ // Run the type generation
97
111
  let types;
98
112
  const absWitPath = resolve(witPath);
99
113
  try {
@@ -104,7 +118,7 @@ export async function typesComponent(witPath, opts) {
104
118
  world: opts.worldName,
105
119
  features,
106
120
  guest: opts.guest ?? false,
107
- asyncMode,
121
+ asyncMode: asyncModeObj,
108
122
  }).map(([name, file]) => [`${outDir}${name}`, file]);
109
123
  } catch (err) {
110
124
  if (err.toString().includes('does not match previous package name')) {
package/src/common.d.ts CHANGED
@@ -14,6 +14,7 @@ export function writeFiles(files: any, summaryTitle: any): Promise<void>;
14
14
  export const isWindows: boolean;
15
15
  export const ASYNC_WASI_IMPORTS: string[];
16
16
  export const ASYNC_WASI_EXPORTS: string[];
17
+ export const DEFAULT_ASYNC_MODE: "sync";
17
18
  export { readFileCli as readFile };
18
19
  declare function readFileCli(file: any, encoding: any): Promise<Buffer<ArrayBufferLike>>;
19
20
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["common.js"],"names":[],"mappings":"AA4BA,+CAEC;AACD,0CAIC;AAED,0CASC;AAED,mEAcC;AAED,wDAoBC;AAED;;;;GAIG;AACH,6CAEC;AAWD,8FAiCC;AAED,yEAmBC;AArJD,gCAA8C;AAE9C,0CASE;AAEF,0CAGE;;AAsEF,yFAMC"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["common.js"],"names":[],"mappings":"AA8BA,+CAEC;AACD,0CAIC;AAED,0CASC;AAED,mEAcC;AAED,wDAoBC;AAED;;;;GAIG;AACH,6CAEC;AAWD,8FAiCC;AAED,yEAmBC;AAvJD,gCAA8C;AAE9C,0CASE;AAEF,0CAGE;AAEF,iCAAkC,MAAM,CAAC;;AAsEzC,yFAMC"}
package/src/common.js CHANGED
@@ -25,6 +25,8 @@ export const ASYNC_WASI_EXPORTS = [
25
25
  'wasi:http/incoming-handler#handle',
26
26
  ];
27
27
 
28
+ export const DEFAULT_ASYNC_MODE = 'sync';
29
+
28
30
  let _showSpinner = false;
29
31
  export function setShowSpinner(val) {
30
32
  _showSpinner = val;
package/src/jco.js CHANGED
@@ -25,7 +25,7 @@ program
25
25
  )
26
26
  .usage('<command> [options]')
27
27
  .enablePositionalOptions()
28
- .version('1.13.2');
28
+ .version('1.14.0');
29
29
 
30
30
  function myParseInt(value) {
31
31
  return parseInt(value, 10);
@@ -69,6 +69,7 @@ program
69
69
  '--debug-starlingmonkey-build',
70
70
  'use a debug build of StarlingMonkey'
71
71
  )
72
+ .option('--engine <path>', 'use a specific StarlingMonkey build')
72
73
  .requiredOption('-o, --out <out>', 'output component file')
73
74
  .option(
74
75
  '--debug-bindings',
@@ -223,7 +224,7 @@ program
223
224
  )
224
225
  .option(
225
226
  '--async-exports <exports...>',
226
- 'EXPERIMENTAL: async component exports (examples: "wasi:cli/run@#run", "handle")'
227
+ 'EXPERIMENTAL: async component exports (examples: "ns:pkg/iface#func", "wasi:cli/run@0.2.3#run", "handle")'
227
228
  )
228
229
  .option('-q, --quiet', 'disable output summary')
229
230
  .option(
@@ -233,6 +234,9 @@ program
233
234
  []
234
235
  )
235
236
  .option('--all-features', 'enable all features')
237
+ .option(
238
+ "--wasm-opt-bin <path-to-wasm-opt>', 'wasm-opt binary path (default: 'binaryen/bin/wasm-opt')"
239
+ )
236
240
  .action(asyncAction(types));
237
241
 
238
242
  program
@@ -251,6 +255,18 @@ program
251
255
  []
252
256
  )
253
257
  .option('--all-features', 'enable all features')
258
+ .option(
259
+ '--async-exports <exports...>',
260
+ 'EXPERIMENTAL: generate async exports (examples: "ns:pkg/iface#func", "wasi:cli/run@0.2.3#run", "handle")'
261
+ )
262
+ .addOption(
263
+ new Option(
264
+ '--async-mode [mode]',
265
+ 'EXPERIMENTAL: use async imports and exports'
266
+ )
267
+ .choices(['sync', 'jspi'])
268
+ .preset('sync')
269
+ )
254
270
  .action(asyncAction(guestTypes));
255
271
 
256
272
  program
@@ -344,6 +360,9 @@ program
344
360
  )
345
361
  .option('--asyncify', 'runs Asyncify pass in wasm-opt')
346
362
  .option('-q, --quiet')
363
+ .option(
364
+ "--wasm-opt-bin <path-to-wasm-opt>', 'wasm-opt binary path (default: 'binaryen/bin/wasm-opt')"
365
+ )
347
366
  .allowExcessArguments(true)
348
367
  .action(asyncAction(opt));
349
368