@bytecodealliance/jco 0.8.0 → 0.9.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 (56) hide show
  1. package/LICENSE +220 -0
  2. package/README.md +38 -14
  3. package/lib/console.js +7 -0
  4. package/obj/imports/cli-base-environment.d.ts +3 -0
  5. package/obj/imports/cli-base-exit.d.ts +4 -0
  6. package/obj/imports/cli-base-preopens.d.ts +5 -0
  7. package/obj/imports/cli-base-stderr.d.ts +5 -0
  8. package/obj/imports/cli-base-stdin.d.ts +5 -0
  9. package/obj/imports/cli-base-stdout.d.ts +5 -0
  10. package/obj/imports/clocks-wall-clock.d.ts +6 -0
  11. package/obj/imports/environment.d.ts +3 -0
  12. package/obj/imports/exit.d.ts +4 -0
  13. package/obj/imports/filesystem-filesystem.d.ts +152 -0
  14. package/obj/imports/filesystem.d.ts +152 -0
  15. package/obj/imports/io-streams.d.ts +12 -0
  16. package/obj/imports/preopens.d.ts +5 -0
  17. package/obj/imports/random-random.d.ts +3 -0
  18. package/obj/imports/random.d.ts +3 -0
  19. package/obj/imports/stderr.d.ts +5 -0
  20. package/obj/imports/stdin.d.ts +5 -0
  21. package/obj/imports/stdout.d.ts +5 -0
  22. package/obj/imports/streams.d.ts +12 -0
  23. package/obj/imports/wall-clock.d.ts +6 -0
  24. package/obj/imports/wasi:cli-base/environment.d.ts +3 -0
  25. package/obj/imports/wasi:cli-base/exit.d.ts +4 -0
  26. package/obj/imports/wasi:cli-base/preopens.d.ts +5 -0
  27. package/obj/imports/wasi:cli-base/stderr.d.ts +5 -0
  28. package/obj/imports/wasi:cli-base/stdin.d.ts +5 -0
  29. package/obj/imports/wasi:cli-base/stdout.d.ts +5 -0
  30. package/obj/imports/wasi:clocks/wall-clock.d.ts +6 -0
  31. package/obj/imports/wasi:filesystem/filesystem.d.ts +152 -0
  32. package/obj/imports/wasi:io/streams.d.ts +12 -0
  33. package/obj/imports/wasi:random/random.d.ts +3 -0
  34. package/{js-component-bindgen-component.core.wasm → obj/js-component-bindgen-component.core.wasm} +0 -0
  35. package/obj/js-component-bindgen-component.d.ts +14 -0
  36. package/obj/js-component-bindgen-component.js +2036 -0
  37. package/obj/wasm-tools.d.ts +19 -0
  38. package/obj/wasm-tools.js +2236 -0
  39. package/package.json +36 -6
  40. package/src/api.js +45 -0
  41. package/src/cmd/componentize.js +21 -0
  42. package/src/cmd/opt.js +151 -0
  43. package/src/cmd/transpile.js +366 -0
  44. package/src/cmd/wasm-tools.js +114 -0
  45. package/src/common.js +104 -0
  46. package/src/jco.js +129 -0
  47. package/api.d.ts +0 -109
  48. package/api.mjs +0 -46035
  49. package/cli.mjs +0 -49700
  50. package/wasm-opt +0 -2
  51. package/wasm2js +0 -2
  52. /package/{wasi_snapshot_preview1.command.wasm → lib/wasi_snapshot_preview1.command.wasm} +0 -0
  53. /package/{wasi_snapshot_preview1.reactor.wasm → lib/wasi_snapshot_preview1.reactor.wasm} +0 -0
  54. /package/{js-component-bindgen-component.core2.wasm → obj/js-component-bindgen-component.core2.wasm} +0 -0
  55. /package/{wasm-tools.core.wasm → obj/wasm-tools.core.wasm} +0 -0
  56. /package/{wasm-tools.core2.wasm → obj/wasm-tools.core2.wasm} +0 -0
@@ -0,0 +1,114 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { readFile } from '../common.js';
3
+ import { $init, print as printFn, parse as parseFn, componentWit as componentWitFn, componentNew as componentNewFn, componentEmbed as componentEmbedFn, metadataAdd as metadataAddFn, metadataShow as metadataShowFn } from "../../obj/wasm-tools.js";
4
+ import { resolve, basename, extname } from 'node:path';
5
+ import c from 'chalk-template';
6
+
7
+ export async function parse(file, opts) {
8
+ await $init;
9
+ const source = (await readFile(file)).toString();
10
+ const output = parseFn(source);
11
+ await writeFile(opts.output, output);
12
+ }
13
+
14
+ export async function print(file, opts) {
15
+ await $init;
16
+ const source = await readFile(file);
17
+ const output = printFn(source);
18
+ if (opts.output) {
19
+ await writeFile(opts.output, output);
20
+ } else {
21
+ console.log(output);
22
+ }
23
+ }
24
+
25
+ export async function componentWit(file, opts) {
26
+ await $init;
27
+ const source = await readFile(file);
28
+ const output = componentWitFn(source, opts.document);
29
+ if (opts.output) {
30
+ await writeFile(opts.output, output);
31
+ } else {
32
+ console.log(output);
33
+ }
34
+ }
35
+
36
+ export async function componentNew(file, opts) {
37
+ await $init;
38
+ const source = file ? await readFile(file) : null;
39
+ let adapters = null;
40
+ if (opts.adapt)
41
+ adapters = await Promise.all(opts.adapt.map(async adapt => {
42
+ let adapter;
43
+ if (adapt.includes('='))
44
+ adapter = adapt.split('=');
45
+ else
46
+ adapter = [basename(adapt).slice(0, -extname(adapt).length), adapt];
47
+ adapter[1] = await readFile(adapter[1]);
48
+ return adapter;
49
+ }));
50
+ const output = componentNewFn(source, adapters);
51
+ await writeFile(opts.output, output);
52
+ }
53
+
54
+ export async function componentEmbed(file, opts) {
55
+ await $init;
56
+ if (opts.metadata)
57
+ opts.metadata = opts.metadata.map(meta => {
58
+ const [field, data = ''] = meta.split('=');
59
+ const [name, version = ''] = data.split('@');
60
+ return [field, [[name, version]]];
61
+ });
62
+ const source = file ? await readFile(file) : null;
63
+ opts.binary = source;
64
+ opts.witPath = resolve(opts.wit);
65
+ const output = componentEmbedFn(opts);
66
+ await writeFile(opts.output, output);
67
+ }
68
+
69
+ export async function metadataAdd(file, opts) {
70
+ await $init;
71
+ const metadata = opts.metadata.map(meta => {
72
+ const [field, data = ''] = meta.split('=');
73
+ const [name, version = ''] = data.split('@');
74
+ return [field, [[name, version]]];
75
+ });
76
+ const source = await readFile(file);
77
+ const output = metadataAddFn(source, metadata);
78
+ await writeFile(opts.output, output);
79
+ }
80
+
81
+ export async function metadataShow(file, opts) {
82
+ await $init;
83
+ const source = await readFile(file);
84
+ let output = '', stack = [1];
85
+ const meta = metadataShowFn(source);
86
+ if (opts.json) {
87
+ console.log(JSON.stringify(meta, null, 2));
88
+ }
89
+ else {
90
+ for (const { name, metaType, producers } of meta) {
91
+ output += ' '.repeat(stack.length - 1);
92
+ const indent = ' '.repeat(stack.length);
93
+ if (metaType.tag === 'component') {
94
+ output += c`{bold [component${name ? ' ' + name : ''}]}\n`;
95
+ if (metaType.val > 0)
96
+ stack.push(metaType.val);
97
+ } else {
98
+ output += c`{bold [module${name ? ' ' + name : ''}]}\n`;
99
+ }
100
+ if (producers.length === 0)
101
+ output += `${indent}(no metadata)\n`;
102
+ for (const [field, items] of producers) {
103
+ for (const [name, version] of items) {
104
+ output += `${indent}${(field + ':').padEnd(13, ' ')} ${name}${version ? c`{cyan ${version}}` : ''}\n`;
105
+ }
106
+ }
107
+ output += '\n';
108
+ if (stack[stack.length - 1] === 0)
109
+ stack.pop();
110
+ stack[stack.length - 1]--;
111
+ }
112
+ process.stdout.write(output);
113
+ }
114
+ }
package/src/common.js ADDED
@@ -0,0 +1,104 @@
1
+ import crypto from 'node:crypto';
2
+ import { resolve } from 'node:path';
3
+ import { tmpdir } from 'node:os';
4
+ import { readFile, writeFile, unlink } from 'node:fs/promises';
5
+ import { spawn } from 'node:child_process';
6
+ import { argv0 } from 'node:process';
7
+ import c from 'chalk-template';
8
+
9
+ let _showSpinner = false;
10
+ export function setShowSpinner (val) {
11
+ _showSpinner = val;
12
+ }
13
+ export function getShowSpinner () {
14
+ const showSpinner = _showSpinner;
15
+ _showSpinner = false;
16
+ return showSpinner;
17
+ }
18
+
19
+ export function sizeStr (num) {
20
+ num /= 1024;
21
+ if (num < 1000)
22
+ return `${fixedDigitDisplay(num, 4)} KiB`;
23
+ num /= 1024;
24
+ if (num < 1000)
25
+ return `${fixedDigitDisplay(num, 4)} MiB`;
26
+ }
27
+
28
+ export function fixedDigitDisplay (num, maxChars) {
29
+ const significantDigits = String(num).split('.')[0].length;
30
+ let str;
31
+ if (significantDigits >= maxChars - 1) {
32
+ str = String(Math.round(num));
33
+ } else {
34
+ const decimalPlaces = maxChars - significantDigits - 1;
35
+ const rounding = 10 ** decimalPlaces;
36
+ str = String(Math.round(num * rounding) / rounding);
37
+ }
38
+ if (maxChars - str.length < 0)
39
+ return str;
40
+ return ' '.repeat(maxChars - str.length) + str;
41
+ }
42
+
43
+ export function table (data, align = []) {
44
+ if (data.length === 0) return '';
45
+ const colLens = data.reduce((maxLens, cur) => maxLens.map((len, i) => Math.max(len, cur[i].length)), data[0].map(cell => cell.length));
46
+ let outTable = '';
47
+ for (const row of data) {
48
+ for (const [i, cell] of row.entries()) {
49
+ if (align[i] === 'right')
50
+ outTable += ' '.repeat(colLens[i] - cell.length) + cell;
51
+ else
52
+ outTable += cell + ' '.repeat(colLens[i] - cell.length);
53
+ }
54
+ outTable += '\n';
55
+ }
56
+ return outTable;
57
+ }
58
+
59
+ export function getTmpFile (source, ext) {
60
+ return resolve(tmpdir(), crypto.createHash('sha256').update(source).update(Math.random().toString()).digest('hex') + ext);
61
+ }
62
+
63
+ async function readFileCli (file, encoding) {
64
+ try {
65
+ return await readFile(file, encoding)
66
+ }
67
+ catch (e) {
68
+ throw c`Unable to read file {bold ${file}}`;
69
+ }
70
+ }
71
+ export { readFileCli as readFile }
72
+
73
+ export async function spawnIOTmp (cmd, input, args) {
74
+ const inFile = getTmpFile(input, '.wasm');
75
+ let outFile = getTmpFile(inFile, '.wasm');
76
+
77
+ await writeFile(inFile, input);
78
+
79
+ const cp = spawn(argv0, [cmd, inFile, ...args, outFile], { stdio: 'pipe' });
80
+
81
+ let stderr = '';
82
+ const p = new Promise((resolve, reject) => {
83
+ cp.stderr.on('data', data => stderr += data.toString());
84
+ cp.on('error', e => {
85
+ reject(e);
86
+ });
87
+ cp.on('exit', code => {
88
+ if (code === 0)
89
+ resolve();
90
+ else
91
+ reject(stderr);
92
+ });
93
+ });
94
+
95
+ try {
96
+ await p;
97
+ var output = await readFile(outFile);
98
+ await Promise.all([unlink(inFile), unlink(outFile)]);
99
+ return output;
100
+ } catch (e) {
101
+ await unlink(inFile);
102
+ throw e;
103
+ }
104
+ }
package/src/jco.js ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import { opt } from './cmd/opt.js';
4
+ import { transpile } from './cmd/transpile.js';
5
+ import { parse, print, componentNew, componentEmbed, metadataAdd, metadataShow, componentWit } from './cmd/wasm-tools.js';
6
+ import { componentize } from './cmd/componentize.js';
7
+ import c from 'chalk-template';
8
+
9
+ program
10
+ .name('jco')
11
+ .description(c`{bold jco - WebAssembly JS Component Tools}\n JS Component Transpilation Bindgen & Wasm Tools for JS`)
12
+ .usage('<command> [options]')
13
+ .version('0.8.0');
14
+
15
+ function myParseInt(value) {
16
+ return parseInt(value, 10);
17
+ }
18
+
19
+ program.command('componentize')
20
+ .description('Create a component from a JavaScript module')
21
+ .usage('<js-source> -o <component-path>')
22
+ .argument('<js-source>', 'JS source file to build')
23
+ .requiredOption('-w, --wit <path>', 'WIT path to build with')
24
+ .option('-n, --world-name <name>', 'WIT world to build')
25
+ .requiredOption('-o, --out <out>', 'output component file')
26
+ .action(asyncAction(componentize));
27
+
28
+ program.command('transpile')
29
+ .description('Transpile a WebAssembly Component to JS + core Wasm for JavaScript execution')
30
+ .usage('<component-path> -o <out-dir>')
31
+ .argument('<component-path>', 'Wasm component binary filepath')
32
+ .option('--name <name>', 'custom output name')
33
+ .requiredOption('-o, --out-dir <out-dir>', 'output directory')
34
+ .option('-m, --minify', 'minify the JS output (--optimize / opt cmd still required)')
35
+ .option('-O, --optimize', 'optimize the component first')
36
+ .option('--no-typescript', 'do not output TypeScript .d.ts types')
37
+ .option('--valid-lifting-optimization', 'optimize component binary validations assuming all lifted values are valid')
38
+ .option('-b, --base64-cutoff <bytes>', 'set the byte size under which core Wasm binaries will be inlined as base64', myParseInt)
39
+ .option('--tla-compat', 'enables compatibility for JS environments without top-level await support via an async $init promise export')
40
+ .option('--no-nodejs-compat', 'disables compatibility in Node.js without a fetch global')
41
+ .option('-M, --map <mappings...>', 'specifier=./output custom mappings for the component imports')
42
+ .option('--no-wasi-shim', 'disable automatic rewriting of WASI imports to use @bytecodealliance/preview2-shim')
43
+ .option('--js', 'output JS instead of core WebAssembly')
44
+ .option('-I, --instantiation', 'output for custom module instantiation')
45
+ .option('-q, --quiet', 'disable logging')
46
+ .option('--', 'for --optimize, custom wasm-opt arguments (defaults to best size optimization)')
47
+ .action(asyncAction(transpile));
48
+
49
+ program.command('opt')
50
+ .description('optimizes a Wasm component, including running wasm-opt Binaryen optimizations')
51
+ .usage('<component-file> -o <output-file>')
52
+ .argument('<component-file>', 'Wasm component binary filepath')
53
+ .requiredOption('-o, --output <output-file>', 'optimized component output filepath')
54
+ .option('-q, --quiet')
55
+ .option('--', 'custom wasm-opt arguments (defaults to best size optimization)')
56
+ .action(asyncAction(opt));
57
+
58
+ program.command('wit')
59
+ .description('extract the WIT from a WebAssembly Component [wasm-tools component wit]')
60
+ .argument('<component-path>', 'Wasm component binary filepath')
61
+ .option('-d, --document <name>', 'WIT document of a package to print')
62
+ .option('-o, --output <output-file>', 'WIT output file path')
63
+ .action(asyncAction(componentWit));
64
+
65
+ program.command('print')
66
+ .description('print the WebAssembly WAT text for a binary file [wasm-tools print]')
67
+ .argument('<input>', 'input file to process')
68
+ .option('-o, --output <output-file>', 'output file path')
69
+ .action(asyncAction(print));
70
+
71
+ program.command('metadata-show')
72
+ .description('extract the producer metadata for a Wasm binary [wasm-tools metadata show]')
73
+ .argument('[module]', 'Wasm component or core module filepath')
74
+ .option('--json', 'output component metadata as JSON')
75
+ .action(asyncAction(metadataShow));
76
+
77
+ program.command('metadata-add')
78
+ .description('add producer metadata for a Wasm binary [wasm-tools metadata add]')
79
+ .argument('[module]', 'Wasm component or core module filepath')
80
+ .requiredOption('-m, --metadata <metadata...>', 'field=name[@version] producer metadata to add with the embedding')
81
+ .requiredOption('-o, --output <output-file>', 'output binary path')
82
+ .action(asyncAction(metadataAdd));
83
+
84
+ program.command('parse')
85
+ .description('parses the Wasm text format into a binary file [wasm-tools parse]')
86
+ .argument('<input>', 'input file to process')
87
+ .requiredOption('-o, --output <output-file>', 'output binary file path')
88
+ .action(asyncAction(parse));
89
+
90
+ program.command('new')
91
+ .description('create a WebAssembly component adapted from a component core Wasm [wasm-tools component new]')
92
+ .argument('<core-module>', 'Wasm core module filepath')
93
+ .requiredOption('-o, --output <output-file>', 'Wasm component output filepath')
94
+ .option('--name <name>', 'custom output name')
95
+ .option('--adapt <[NAME=]adapter...>', 'component adapters to apply')
96
+ .action(asyncAction(componentNew));
97
+
98
+ program.command('embed')
99
+ .description('embed the component typing section into a core Wasm module [wasm-tools component embed]')
100
+ .argument('[core-module]', 'Wasm core module filepath')
101
+ .requiredOption('-o, --output <output-file>', 'Wasm component output filepath')
102
+ .requiredOption('--wit <wit-world>', 'WIT world path')
103
+ .option('--dummy', 'generate a dummy component')
104
+ .option('--string-encoding <utf8|utf16|compact-utf16>', 'set the component string encoding')
105
+ .option('--world <world-name>', 'positional world path to embed')
106
+ .option('-m, --metadata <metadata...>', 'field=name[@version] producer metadata to add with the embedding')
107
+ .action(asyncAction(componentEmbed));
108
+
109
+ program.parse();
110
+
111
+ function asyncAction (cmd) {
112
+ return function () {
113
+ const args = [...arguments];
114
+ (async () => {
115
+ try {
116
+ await cmd.apply(null, args);
117
+ }
118
+ catch (e) {
119
+ process.stdout.write(`(jco ${cmd.name}) `);
120
+ if (typeof e === 'string') {
121
+ console.error(c`{red.bold Error}: ${e}\n`);
122
+ } else {
123
+ console.error(e);
124
+ }
125
+ process.exit(1);
126
+ }
127
+ })();
128
+ };
129
+ }
package/api.d.ts DELETED
@@ -1,109 +0,0 @@
1
- /**
2
- * Optimize a Component with Binaryen wasm-opt optimizations
3
- */
4
- export function opt(componentBytes: Uint8Array, opts?: { quiet: boolean; optArgs?: string[] }): Promise<{
5
- component: Uint8Array,
6
- compressionInfo: { beforeBytes: number, afterBytes: number }[]
7
- }>;
8
-
9
- export interface TranspileOpts {
10
- /// name for the generated JS file.
11
- name?: string,
12
- /// instead of a direct ES module, output the raw
13
- /// instantiation function for custom virtualization.
14
- instantiation?: boolean,
15
- /// remap Component imports
16
- map?: Record<string, string>,
17
- /// optimization to reduce code size
18
- validLiftingOptimization?: boolean,
19
- /// disables Node.js compatible output
20
- noNodejsCompat?: boolean,
21
- /// enable compat in JS runtimes without TLA support
22
- tlaCompat?: boolean,
23
- /// size in bytes, under which Wasm modules get inlined as base64.
24
- base64Cutoff?: number,
25
- /// use asm.js instead of core WebAssembly for execution.
26
- asm?: boolean,
27
- /// minify the output JS.
28
- minify?: boolean,
29
- /// optimize the Component with Binaryen wasm-opt first.
30
- optimize?: boolean,
31
- /// if using optimize, custom optimization options
32
- /// (defaults to best optimization, but this is very slow)
33
- optArgs?: string[],
34
- /// rewrites WASI imports to the @bytecodealliance/preview2-shim
35
- /// defaults to true
36
- wasiShim?: boolean
37
- }
38
-
39
- export interface EmbedOpts {
40
- /// Component binary to embed
41
- binary?: Uint8Array,
42
- /// Pass an inline WIT source
43
- witSource?: string,
44
- /// Pass the file system path to WIT file
45
- /// Either a path or a source must be passed not both
46
- witPath?: string,
47
- /// String encoding for the component functions
48
- stringEncoding?: 'utf8' | 'utf16',
49
- /// Generate a dummy component
50
- dummy: option<bool>,
51
- /// World name to embed
52
- world?: string,
53
- /// Metadata to embed
54
- metadata?: Metadata
55
- }
56
-
57
- /**
58
- * Transpile a Component into a JS-executable package
59
- */
60
- export function transpile(component: Uint8Array, opts?: TranspileOpts): Promise<{
61
- files: Record<string, Uint8Array>,
62
- imports: string[],
63
- exports: [string, 'function' | 'instance'][]
64
- }>;
65
-
66
- /**
67
- * Parse a WAT string into a Wasm binary
68
- */
69
- export function parse(wat: string): Uint8Array;
70
-
71
- /**
72
- * Print a Wasm binary as a WAT string
73
- */
74
- export function print(binary: Uint8Array | ArrayBuffer): string;
75
-
76
- /**
77
- * WIT Component - create a Component from a Wasm core binary
78
- */
79
- export function componentNew(binary: Uint8Array | ArrayBuffer, adapters?: [string, Uint8Array][] | null): Uint8Array;
80
-
81
- type Metadata = [string, [string, string][]][];
82
-
83
- /**
84
- * Embed a world into a Wasm core binary
85
- */
86
- export function componentEmbed(opts: EmbedOpts): Uint8Array;
87
-
88
- /**
89
- * Extract the producer metadata for a Wasm component or core module
90
- */
91
- export function metadataShow(binary: Uint8Array | ArrayBuffer): {
92
- name?: string,
93
- metaType: { tag: 'module' } | { tag: 'component', val: number },
94
- metadata: Metadata
95
- }[];
96
-
97
- /**
98
- * Extract the producer metadata for a Wasm component or core module
99
- */
100
- export function metadataAdd(binary: Uint8Array | ArrayBuffer, metadata: Metadata): Uint8Array;
101
-
102
- /**
103
- * Extract the WIT world from a Wasm Component
104
- */
105
- export function componentWit(binary: Uint8Array | ArrayBuffer, document?: string | null): string;
106
-
107
- export type StringEncoding = 'utf8' | 'utf16' | 'compact-utf16';
108
-
109
- export const $init: Promise<void>;