@bytecodealliance/jco 1.10.2 → 1.11.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 +18 -6
- package/src/cmd/componentize.js +64 -18
- package/src/cmd/opt.js +233 -202
- package/src/cmd/run.js +117 -73
- package/src/cmd/transpile.js +643 -530
- package/src/cmd/wasm-tools.js +130 -95
- package/src/jco.js +5 -1
- package/obj/interfaces/local-wasm-tools-tools.d.ts +0 -52
- package/obj/interfaces/wasi-cli-environment.d.ts +0 -2
- package/obj/interfaces/wasi-cli-exit.d.ts +0 -3
- package/obj/interfaces/wasi-cli-stderr.d.ts +0 -3
- package/obj/interfaces/wasi-cli-stdin.d.ts +0 -3
- package/obj/interfaces/wasi-cli-stdout.d.ts +0 -3
- package/obj/interfaces/wasi-cli-terminal-input.d.ts +0 -8
- package/obj/interfaces/wasi-cli-terminal-output.d.ts +0 -8
- package/obj/interfaces/wasi-cli-terminal-stderr.d.ts +0 -3
- package/obj/interfaces/wasi-cli-terminal-stdin.d.ts +0 -3
- package/obj/interfaces/wasi-cli-terminal-stdout.d.ts +0 -3
- package/obj/interfaces/wasi-clocks-wall-clock.d.ts +0 -5
- package/obj/interfaces/wasi-filesystem-preopens.d.ts +0 -3
- package/obj/interfaces/wasi-filesystem-types.d.ts +0 -164
- package/obj/interfaces/wasi-io-error.d.ts +0 -8
- package/obj/interfaces/wasi-io-streams.d.ts +0 -30
- package/obj/interfaces/wasi-random-random.d.ts +0 -2
- package/obj/js-component-bindgen-component.core.wasm +0 -0
- package/obj/js-component-bindgen-component.core2.wasm +0 -0
- package/obj/js-component-bindgen-component.d.ts +0 -116
- package/obj/js-component-bindgen-component.js +0 -4302
- package/obj/wasm-tools.core.wasm +0 -0
- package/obj/wasm-tools.core2.wasm +0 -0
- package/obj/wasm-tools.d.ts +0 -20
- package/obj/wasm-tools.js +0 -4369
- package/src/api.d.ts +0 -42
- package/src/api.d.ts.map +0 -1
- package/src/browser.d.ts +0 -4
- package/src/browser.d.ts.map +0 -1
- package/src/cmd/componentize.d.ts +0 -2
- package/src/cmd/componentize.d.ts.map +0 -1
- package/src/cmd/opt.d.ts +0 -20
- package/src/cmd/opt.d.ts.map +0 -1
- package/src/cmd/run.d.ts +0 -3
- package/src/cmd/run.d.ts.map +0 -1
- package/src/cmd/transpile.d.ts +0 -89
- package/src/cmd/transpile.d.ts.map +0 -1
- package/src/cmd/wasm-tools.d.ts +0 -8
- package/src/cmd/wasm-tools.d.ts.map +0 -1
- package/src/common.d.ts +0 -16
- package/src/common.d.ts.map +0 -1
- package/src/jco.d.ts +0 -3
- package/src/jco.d.ts.map +0 -1
- package/src/ora-shim.d.ts +0 -7
- package/src/ora-shim.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytecodealliance/jco",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "JavaScript tooling for working with WebAssembly Components",
|
|
5
5
|
"author": "Guy Bedford",
|
|
6
6
|
"bin": {
|
|
@@ -38,10 +38,14 @@
|
|
|
38
38
|
"@typescript-eslint/eslint-plugin": "^8.2.0",
|
|
39
39
|
"@typescript-eslint/parser": "^8.2.0",
|
|
40
40
|
"eslint": "^9.9.0",
|
|
41
|
+
"eslint-config-prettier": "^10.1.1",
|
|
42
|
+
"eslint-plugin-prettier": "^5.2.3",
|
|
41
43
|
"mime": "^4.0.4",
|
|
42
|
-
"
|
|
44
|
+
"prettier": "^3.5.3",
|
|
43
45
|
"puppeteer": "^24.0.1",
|
|
44
|
-
"
|
|
46
|
+
"smol-toml": "^1.3.1",
|
|
47
|
+
"typescript": "^5.5.4",
|
|
48
|
+
"vitest": "^3.0.7"
|
|
45
49
|
},
|
|
46
50
|
"repository": {
|
|
47
51
|
"type": "git",
|
|
@@ -61,9 +65,10 @@
|
|
|
61
65
|
"build": "cargo xtask build debug",
|
|
62
66
|
"build:release": "cargo xtask build release",
|
|
63
67
|
"build:types:preview2-shim": "cargo xtask generate wasi-types",
|
|
64
|
-
"lint": "eslint -c eslintrc.
|
|
65
|
-
"
|
|
66
|
-
"test": "
|
|
68
|
+
"lint": "eslint -c eslintrc.mjs src/**/*.js packages/*/lib/**/*.js",
|
|
69
|
+
"lint:fix": "npm run lint -- --fix",
|
|
70
|
+
"test": "vitest run -c test/vitest.ts",
|
|
71
|
+
"test:lts": "vitest run -c test/vitest.lts.ts",
|
|
67
72
|
"prepublishOnly": "cargo xtask build release && npm run test"
|
|
68
73
|
},
|
|
69
74
|
"files": [
|
|
@@ -75,6 +80,13 @@
|
|
|
75
80
|
"obj/interfaces"
|
|
76
81
|
],
|
|
77
82
|
"workspaces": [
|
|
83
|
+
".",
|
|
84
|
+
"examples/components/add",
|
|
85
|
+
"examples/components/http-hello-world",
|
|
86
|
+
"examples/components/node-fetch",
|
|
87
|
+
"examples/components/string-reverse",
|
|
88
|
+
"examples/components/string-reverse-upper",
|
|
89
|
+
"examples/components/webidl-book-library",
|
|
78
90
|
"packages/preview2-shim"
|
|
79
91
|
]
|
|
80
92
|
}
|
package/src/cmd/componentize.js
CHANGED
|
@@ -1,23 +1,69 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { resolve, basename } from 'node:path';
|
|
3
|
+
|
|
3
4
|
import c from 'chalk-template';
|
|
4
5
|
|
|
5
|
-
export async function componentize
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
export async function componentize(jsSource, opts) {
|
|
7
|
+
const { componentize: componentizeFn } = await eval(
|
|
8
|
+
'import("@bytecodealliance/componentize-js")'
|
|
9
|
+
);
|
|
10
|
+
if (opts.disable?.includes('all')) {
|
|
11
|
+
opts.disable = ['stdio', 'random', 'clocks', 'http'];
|
|
12
|
+
}
|
|
13
|
+
const source = await readFile(jsSource, 'utf8');
|
|
14
|
+
|
|
15
|
+
const witPath = resolve(opts.wit);
|
|
16
|
+
const sourceName = basename(jsSource);
|
|
17
|
+
|
|
18
|
+
let component;
|
|
19
|
+
try {
|
|
20
|
+
const result = await componentizeFn(source, {
|
|
21
|
+
enableAot: opts.aot,
|
|
22
|
+
aotMinStackSizeBytes: opts.aotMinStackSizeBytes,
|
|
23
|
+
wevalBin: opts.wevalBin,
|
|
24
|
+
sourceName,
|
|
25
|
+
witPath,
|
|
26
|
+
worldName: opts.worldName,
|
|
27
|
+
disableFeatures: opts.disable,
|
|
28
|
+
enableFeatures: opts.enable,
|
|
29
|
+
preview2Adapter: opts.preview2Adapter,
|
|
30
|
+
debugBuild: opts.debugStarlingmonkeyBuild,
|
|
31
|
+
});
|
|
32
|
+
component = result.component;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
// Detect package resolution issues that usually mean a misconfigured "witPath"
|
|
35
|
+
if (err.toString().includes('no known packages')) {
|
|
36
|
+
const isFile = await stat(witPath).then((s) => s.isFile());
|
|
37
|
+
if (isFile) {
|
|
38
|
+
const hint = await printWITPathHint(witPath);
|
|
39
|
+
if (err.message) {
|
|
40
|
+
err.message += `\n${hint}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await writeFile(opts.out, component);
|
|
48
|
+
|
|
49
|
+
console.log(c`{green OK} Successfully written {bold ${opts.out}}.`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Print a hint about the witPath option that may be incorrect
|
|
54
|
+
*
|
|
55
|
+
* @param {string} witPath - witPath option that was used (which is a path that resolves to a file or directory)
|
|
56
|
+
* @returns {string} user-visible, highlighted output that can be printed
|
|
57
|
+
*/
|
|
58
|
+
async function printWITPathHint(witPath) {
|
|
59
|
+
const pathMeta = await stat(witPath);
|
|
60
|
+
let output = '\n';
|
|
61
|
+
if (!pathMeta.isFile() && !pathMeta.isDirectory()) {
|
|
62
|
+
output += c`{yellow.bold warning} The supplited WIT path [${witPath}] is neither a file or directory.\n`;
|
|
63
|
+
return output;
|
|
64
|
+
}
|
|
65
|
+
output += c`{yellow.bold warning} Your WIT path option [${witPath}] may be incorrect\n`;
|
|
66
|
+
output += c`{yellow.bold warning} When using a world with dependencies, you must pass the enclosing WIT folder, not a single file.\n`;
|
|
67
|
+
output += c`{yellow.bold warning} (e.g. 'wit/', rather than 'wit/component.wit').\n`;
|
|
68
|
+
return output;
|
|
23
69
|
}
|
package/src/cmd/opt.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { $init, tools } from
|
|
1
|
+
import { $init, tools } from '../../obj/wasm-tools.js';
|
|
2
2
|
const { metadataShow, print } = tools;
|
|
3
|
-
import { writeFile } from
|
|
4
|
-
import { fileURLToPath } from
|
|
5
|
-
import c from
|
|
3
|
+
import { writeFile } from 'fs/promises';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import c from 'chalk-template';
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from
|
|
15
|
-
import ora from
|
|
7
|
+
readFile,
|
|
8
|
+
sizeStr,
|
|
9
|
+
fixedDigitDisplay,
|
|
10
|
+
table,
|
|
11
|
+
spawnIOTmp,
|
|
12
|
+
setShowSpinner,
|
|
13
|
+
getShowSpinner,
|
|
14
|
+
} from '../common.js';
|
|
15
|
+
import ora from '#ora';
|
|
16
16
|
|
|
17
17
|
export async function opt(componentPath, opts, program) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
await $init;
|
|
19
|
+
const varIdx = program.parent.rawArgs.indexOf('--');
|
|
20
|
+
if (varIdx !== -1) opts.optArgs = program.parent.rawArgs.slice(varIdx + 1);
|
|
21
|
+
const componentBytes = await readFile(componentPath);
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (!opts.quiet) setShowSpinner(true);
|
|
24
|
+
const optPromise = optimizeComponent(componentBytes, opts);
|
|
25
|
+
const { component, compressionInfo } = await optPromise;
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
await writeFile(opts.output, component);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
let totalBeforeBytes = 0,
|
|
30
|
+
totalAfterBytes = 0;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (!opts.quiet)
|
|
33
|
+
console.log(c`
|
|
34
34
|
{bold Optimized WebAssembly Component Internal Core Modules:}
|
|
35
35
|
|
|
36
36
|
${table(
|
|
37
|
-
[
|
|
38
|
-
...compressionInfo.map(({ beforeBytes, afterBytes }, i) => {
|
|
39
|
-
totalBeforeBytes += beforeBytes;
|
|
40
|
-
totalAfterBytes += afterBytes;
|
|
41
|
-
return [
|
|
42
|
-
` - Core Module ${i + 1}: `,
|
|
43
|
-
sizeStr(beforeBytes),
|
|
44
|
-
" -> ",
|
|
45
|
-
c`{cyan ${sizeStr(afterBytes)}} `,
|
|
46
|
-
`(${fixedDigitDisplay((afterBytes / beforeBytes) * 100, 2)}%)`,
|
|
47
|
-
];
|
|
48
|
-
}),
|
|
49
|
-
["", "", "", "", ""],
|
|
50
37
|
[
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
...compressionInfo.map(({ beforeBytes, afterBytes }, i) => {
|
|
39
|
+
totalBeforeBytes += beforeBytes;
|
|
40
|
+
totalAfterBytes += afterBytes;
|
|
41
|
+
return [
|
|
42
|
+
` - Core Module ${i + 1}: `,
|
|
43
|
+
sizeStr(beforeBytes),
|
|
44
|
+
' -> ',
|
|
45
|
+
c`{cyan ${sizeStr(afterBytes)}} `,
|
|
46
|
+
`(${fixedDigitDisplay((afterBytes / beforeBytes) * 100, 2)}%)`,
|
|
47
|
+
];
|
|
48
|
+
}),
|
|
49
|
+
['', '', '', '', ''],
|
|
50
|
+
[
|
|
51
|
+
` = Total: `,
|
|
52
|
+
`${sizeStr(totalBeforeBytes)}`,
|
|
53
|
+
` => `,
|
|
54
|
+
c`{cyan ${sizeStr(totalAfterBytes)}} `,
|
|
55
|
+
`(${fixedDigitDisplay((totalAfterBytes / totalBeforeBytes) * 100, 2)}%)`,
|
|
56
|
+
],
|
|
56
57
|
],
|
|
57
|
-
|
|
58
|
-
[, , , , "right"]
|
|
58
|
+
[, , , , 'right']
|
|
59
59
|
)}`);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -65,12 +65,12 @@ ${table(
|
|
|
65
65
|
* @returns {number}
|
|
66
66
|
*/
|
|
67
67
|
function byteLengthLEB128(val) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
let len = 0;
|
|
69
|
+
do {
|
|
70
|
+
val >>>= 7;
|
|
71
|
+
len++;
|
|
72
|
+
} while (val !== 0);
|
|
73
|
+
return len;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
@@ -80,165 +80,190 @@ function byteLengthLEB128(val) {
|
|
|
80
80
|
* @returns {Promise<{ component: Uint8Array, compressionInfo: { beforeBytes: number, afterBytes: number }[] >}
|
|
81
81
|
*/
|
|
82
82
|
export async function optimizeComponent(componentBytes, opts) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
83
|
+
await $init;
|
|
84
|
+
const showSpinner = getShowSpinner();
|
|
85
|
+
let spinner;
|
|
86
|
+
try {
|
|
87
|
+
let componentMetadata = metadataShow(componentBytes);
|
|
88
|
+
componentMetadata.forEach((metadata, index) => {
|
|
89
|
+
// add index to the metadata object
|
|
90
|
+
metadata.index = index;
|
|
91
|
+
const size = metadata.range[1] - metadata.range[0];
|
|
92
|
+
// compute previous LEB128 encoding length
|
|
93
|
+
metadata.prevLEBLen = byteLengthLEB128(size);
|
|
94
|
+
});
|
|
95
|
+
const coreModules = componentMetadata.filter(
|
|
96
|
+
({ metaType }) => metaType.tag === 'module'
|
|
97
|
+
);
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
// log number of core Wasm modules to be run with wasm-opt
|
|
100
|
+
let completed = 0;
|
|
101
|
+
const spinnerText = () =>
|
|
102
|
+
c`{cyan ${completed} / ${coreModules.length}} Running Binaryen on WebAssembly Component Internal Core Modules \n`;
|
|
103
|
+
if (showSpinner) {
|
|
104
|
+
spinner = ora({
|
|
105
|
+
color: 'cyan',
|
|
106
|
+
spinner: 'bouncingBar',
|
|
107
|
+
}).start();
|
|
108
|
+
spinner.text = spinnerText();
|
|
109
|
+
}
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
// gather the options for wasm-opt. optionally, adding the asyncify flag
|
|
112
|
+
const args = opts?.optArgs
|
|
113
|
+
? [...opts.optArgs]
|
|
114
|
+
: [
|
|
115
|
+
'-Oz',
|
|
116
|
+
'--low-memory-unused',
|
|
117
|
+
'--enable-bulk-memory',
|
|
118
|
+
'--strip-debug',
|
|
119
|
+
];
|
|
120
|
+
if (opts?.asyncify) args.push('--asyncify');
|
|
115
121
|
|
|
122
|
+
// process core Wasm modules with wasm-opt
|
|
123
|
+
await Promise.all(
|
|
124
|
+
coreModules.map(async (metadata) => {
|
|
125
|
+
if (metadata.metaType.tag === 'module') {
|
|
126
|
+
// store the wasm-opt processed module in the metadata
|
|
127
|
+
metadata.optimized = await wasmOpt(
|
|
128
|
+
componentBytes.subarray(
|
|
129
|
+
metadata.range[0],
|
|
130
|
+
metadata.range[1]
|
|
131
|
+
),
|
|
132
|
+
args
|
|
133
|
+
);
|
|
116
134
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
args);
|
|
135
|
+
// compute the size change, including the change to
|
|
136
|
+
// the LEB128 encoding of the size change
|
|
137
|
+
const prevModuleSize =
|
|
138
|
+
metadata.range[1] - metadata.range[0];
|
|
139
|
+
const newModuleSize = metadata.optimized.byteLength;
|
|
140
|
+
metadata.newLEBLen = byteLengthLEB128(newModuleSize);
|
|
141
|
+
metadata.sizeChange = newModuleSize - prevModuleSize;
|
|
125
142
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
143
|
+
if (spinner) {
|
|
144
|
+
completed++;
|
|
145
|
+
spinner.text = spinnerText();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
);
|
|
132
150
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
// organize components in modules into tree parent and children
|
|
152
|
+
const nodes = componentMetadata.slice(1);
|
|
153
|
+
const getChildren = (parentIndex) => {
|
|
154
|
+
const children = [];
|
|
155
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
156
|
+
const metadata = nodes[i];
|
|
157
|
+
if (metadata.parentIndex === parentIndex) {
|
|
158
|
+
nodes.splice(i, 1); // remove from nodes
|
|
159
|
+
i--;
|
|
160
|
+
metadata.children = getChildren(metadata.index);
|
|
161
|
+
metadata.sizeChange = metadata.children.reduce(
|
|
162
|
+
(total, { prevLEBLen, newLEBLen, sizeChange }) => {
|
|
163
|
+
return sizeChange
|
|
164
|
+
? total + sizeChange + newLEBLen - prevLEBLen
|
|
165
|
+
: total;
|
|
166
|
+
},
|
|
167
|
+
metadata.sizeChange || 0
|
|
168
|
+
);
|
|
169
|
+
const prevSize = metadata.range[1] - metadata.range[0];
|
|
170
|
+
metadata.newLEBLen = byteLengthLEB128(
|
|
171
|
+
prevSize + metadata.sizeChange
|
|
172
|
+
);
|
|
173
|
+
children.push(metadata);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return children;
|
|
177
|
+
};
|
|
178
|
+
const componentTree = getChildren(0);
|
|
139
179
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
145
|
-
const metadata = nodes[i];
|
|
146
|
-
if (metadata.parentIndex === parentIndex) {
|
|
147
|
-
nodes.splice(i, 1); // remove from nodes
|
|
148
|
-
i--;
|
|
149
|
-
metadata.children = getChildren(metadata.index);
|
|
150
|
-
metadata.sizeChange = metadata.children
|
|
151
|
-
.reduce((total, {prevLEBLen, newLEBLen, sizeChange}) => {
|
|
152
|
-
return sizeChange ?
|
|
153
|
-
total + sizeChange + newLEBLen - prevLEBLen :
|
|
154
|
-
total;
|
|
180
|
+
// compute the total size change in the component binary
|
|
181
|
+
const sizeChange = componentTree.reduce(
|
|
182
|
+
(total, { prevLEBLen, newLEBLen, sizeChange }) => {
|
|
183
|
+
return total + (sizeChange || 0) + newLEBLen - prevLEBLen;
|
|
155
184
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const componentTree = getChildren(0);
|
|
185
|
+
0
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
let outComponentBytes = new Uint8Array(
|
|
189
|
+
componentBytes.byteLength + sizeChange
|
|
190
|
+
);
|
|
191
|
+
let nextReadPos = 0,
|
|
192
|
+
nextWritePos = 0;
|
|
165
193
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
194
|
+
const write = ({
|
|
195
|
+
prevLEBLen,
|
|
196
|
+
range,
|
|
197
|
+
optimized,
|
|
198
|
+
children,
|
|
199
|
+
sizeChange,
|
|
200
|
+
}) => {
|
|
201
|
+
// write from the last read to the LEB byte start
|
|
202
|
+
outComponentBytes.set(
|
|
203
|
+
componentBytes.subarray(nextReadPos, range[0] - prevLEBLen),
|
|
204
|
+
nextWritePos
|
|
205
|
+
);
|
|
206
|
+
nextWritePos += range[0] - prevLEBLen - nextReadPos;
|
|
171
207
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
208
|
+
// write the new LEB bytes
|
|
209
|
+
let val = range[1] - range[0] + sizeChange;
|
|
210
|
+
do {
|
|
211
|
+
const byte = val & 0x7f;
|
|
212
|
+
val >>>= 7;
|
|
213
|
+
outComponentBytes[nextWritePos++] =
|
|
214
|
+
val === 0 ? byte : byte | 0x80;
|
|
215
|
+
} while (val !== 0);
|
|
175
216
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
217
|
+
if (optimized) {
|
|
218
|
+
// write the core module
|
|
219
|
+
outComponentBytes.set(optimized, nextWritePos);
|
|
220
|
+
nextReadPos = range[1];
|
|
221
|
+
nextWritePos += optimized.byteLength;
|
|
222
|
+
} else if (children.length > 0) {
|
|
223
|
+
// write child components / modules
|
|
224
|
+
nextReadPos = range[0];
|
|
225
|
+
children.forEach(write);
|
|
226
|
+
} else {
|
|
227
|
+
// write component
|
|
228
|
+
outComponentBytes.set(
|
|
229
|
+
componentBytes.subarray(range[0], range[1]),
|
|
230
|
+
nextWritePos
|
|
231
|
+
);
|
|
232
|
+
nextReadPos = range[1];
|
|
233
|
+
nextWritePos += range[1] - range[0];
|
|
234
|
+
}
|
|
235
|
+
};
|
|
183
236
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
do {
|
|
187
|
-
const byte = val & 0x7f;
|
|
188
|
-
val >>>= 7;
|
|
189
|
-
outComponentBytes[nextWritePos++] = val === 0 ? byte : byte | 0x80;
|
|
190
|
-
} while (val !== 0);
|
|
237
|
+
// write each top-level component / module
|
|
238
|
+
componentTree.forEach(write);
|
|
191
239
|
|
|
192
|
-
|
|
193
|
-
// write the core module
|
|
194
|
-
outComponentBytes.set(optimized, nextWritePos);
|
|
195
|
-
nextReadPos = range[1];
|
|
196
|
-
nextWritePos += optimized.byteLength;
|
|
197
|
-
} else if (children.length > 0) {
|
|
198
|
-
// write child components / modules
|
|
199
|
-
nextReadPos = range[0];
|
|
200
|
-
children.forEach(write);
|
|
201
|
-
} else {
|
|
202
|
-
// write component
|
|
240
|
+
// write remaining
|
|
203
241
|
outComponentBytes.set(
|
|
204
|
-
|
|
205
|
-
|
|
242
|
+
componentBytes.subarray(nextReadPos),
|
|
243
|
+
nextWritePos
|
|
206
244
|
);
|
|
207
|
-
nextReadPos = range[1];
|
|
208
|
-
nextWritePos += range[1] - range[0];
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// write each top-level component / module
|
|
213
|
-
componentTree.forEach(write);
|
|
214
245
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
246
|
+
// verify it still parses ok
|
|
247
|
+
if (!opts?.noVerify) {
|
|
248
|
+
try {
|
|
249
|
+
await print(outComponentBytes);
|
|
250
|
+
} catch (e) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Internal error performing optimization.\n${e.message}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
220
256
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
257
|
+
return {
|
|
258
|
+
component: outComponentBytes,
|
|
259
|
+
compressionInfo: coreModules.map(({ range, optimized }) => ({
|
|
260
|
+
beforeBytes: range[1] - range[0],
|
|
261
|
+
afterBytes: optimized?.byteLength,
|
|
262
|
+
})),
|
|
263
|
+
};
|
|
264
|
+
} finally {
|
|
265
|
+
if (spinner) spinner.stop();
|
|
230
266
|
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
component: outComponentBytes,
|
|
234
|
-
compressionInfo: coreModules.map(({range, optimized}) => ({
|
|
235
|
-
beforeBytes: range[1] - range[0],
|
|
236
|
-
afterBytes: optimized?.byteLength,
|
|
237
|
-
})),
|
|
238
|
-
};
|
|
239
|
-
} finally {
|
|
240
|
-
if (spinner) spinner.stop();
|
|
241
|
-
}
|
|
242
267
|
}
|
|
243
268
|
|
|
244
269
|
/**
|
|
@@ -247,15 +272,21 @@ export async function optimizeComponent(componentBytes, opts) {
|
|
|
247
272
|
* @returns {Promise<Uint8Array>}
|
|
248
273
|
*/
|
|
249
274
|
async function wasmOpt(source, args) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
275
|
+
const wasmOptPath = fileURLToPath(
|
|
276
|
+
import.meta.resolve('binaryen/bin/wasm-opt')
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
return await spawnIOTmp(wasmOptPath, source, [...args, '-o']);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
if (e.toString().includes('BasicBlock requested'))
|
|
283
|
+
return wasmOpt(source, args);
|
|
284
|
+
throw e;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
253
287
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return wasmOpt(source, args);
|
|
259
|
-
throw e;
|
|
260
|
-
}
|
|
288
|
+
// see: https://github.com/vitest-dev/vitest/issues/6953#issuecomment-2505310022
|
|
289
|
+
if (typeof __vite_ssr_import_meta__ !== 'undefined') {
|
|
290
|
+
__vite_ssr_import_meta__.resolve = (path) =>
|
|
291
|
+
'file://' + globalCreateRequire(import.meta.url).resolve(path);
|
|
261
292
|
}
|