@reteps/dockerfmt 0.3.6 → 0.5.1
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 +1 -1
- package/dist/format.js +10 -17
- package/dist/format.test.d.ts +1 -0
- package/dist/format.test.js +69 -0
- package/dist/format.wasm +0 -0
- package/dist/wasm_exec.js +342 -287
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@reteps/dockerfmt`
|
|
2
2
|
|
|
3
|
-
Bindings around the Golang `dockerfmt` tooling. It
|
|
3
|
+
Bindings around the Golang `dockerfmt` tooling. It compiles the Go code to WebAssembly (using standard Go's `GOOS=js GOARCH=wasm` target), which is then used in the JS bindings.
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
```js
|
package/dist/format.js
CHANGED
|
@@ -1,29 +1,22 @@
|
|
|
1
1
|
import './wasm_exec.js';
|
|
2
2
|
export const formatDockerfileContents = async (fileContents, options, getWasm) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
3
|
+
// Use our namespaced Go class instead of globalThis.Go to avoid conflicts
|
|
4
|
+
// with other Go WASM packages (see wasm_exec.js modifications).
|
|
5
|
+
const GoClass = globalThis.__dockerfmt_Go;
|
|
6
|
+
const go = new GoClass();
|
|
7
7
|
const wasmBuffer = await getWasm();
|
|
8
8
|
const wasm = await WebAssembly.instantiate(wasmBuffer, go.importObject);
|
|
9
9
|
/**
|
|
10
10
|
* Do not await this promise, because it only resolves once the go main()
|
|
11
11
|
* function has exited. But we need the main function to stay alive to be
|
|
12
|
-
* able to call the
|
|
12
|
+
* able to call the formatBytes function.
|
|
13
13
|
*/
|
|
14
14
|
go.run(wasm.instance);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const resultPointer = formatBytes(filePointer, fileBufferBytes.byteLength, options.indent, options.trailingNewline, options.spaceRedirects);
|
|
21
|
-
// Decode the result
|
|
22
|
-
const resultBytes = new Uint8Array(memory.buffer).subarray(resultPointer);
|
|
23
|
-
const end = resultBytes.indexOf(0);
|
|
24
|
-
const result = decoder.decode(resultBytes.subarray(0, end));
|
|
25
|
-
free(filePointer);
|
|
26
|
-
return result;
|
|
15
|
+
const formatBytes = globalThis.__dockerfmt_formatBytes;
|
|
16
|
+
if (typeof formatBytes !== 'function') {
|
|
17
|
+
throw new Error('dockerfmt WASM module did not register formatBytes');
|
|
18
|
+
}
|
|
19
|
+
return formatBytes(fileContents, options.indent, options.trailingNewline, options.spaceRedirects);
|
|
27
20
|
};
|
|
28
21
|
export const formatDockerfile = () => {
|
|
29
22
|
throw new Error('`formatDockerfile` is not implemented in the browser. Use `formatDockerfileContents` instead.');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { formatDockerfileContents } from './node.js';
|
|
4
|
+
const defaultOptions = {
|
|
5
|
+
indent: 4,
|
|
6
|
+
trailingNewline: true,
|
|
7
|
+
spaceRedirects: false,
|
|
8
|
+
};
|
|
9
|
+
describe('formatDockerfileContents', () => {
|
|
10
|
+
it('formats a basic Dockerfile', async () => {
|
|
11
|
+
const input = `from alpine
|
|
12
|
+
run echo hello
|
|
13
|
+
`.trim();
|
|
14
|
+
const result = await formatDockerfileContents(input, defaultOptions);
|
|
15
|
+
assert.equal(result, 'FROM alpine\nRUN echo hello\n');
|
|
16
|
+
});
|
|
17
|
+
it('formats CMD JSON form with spaces', async () => {
|
|
18
|
+
const input = `FROM alpine
|
|
19
|
+
CMD ["ls","-la"]
|
|
20
|
+
`.trim();
|
|
21
|
+
const result = await formatDockerfileContents(input, defaultOptions);
|
|
22
|
+
assert.equal(result, 'FROM alpine\nCMD ["ls", "-la"]\n');
|
|
23
|
+
});
|
|
24
|
+
it('formats RUN JSON form with spaces', async () => {
|
|
25
|
+
const input = `FROM alpine
|
|
26
|
+
RUN ["echo","hello"]
|
|
27
|
+
`.trim();
|
|
28
|
+
const result = await formatDockerfileContents(input, defaultOptions);
|
|
29
|
+
assert.equal(result, 'FROM alpine\nRUN ["echo", "hello"]\n');
|
|
30
|
+
});
|
|
31
|
+
it('handles the issue #25 reproduction case', async () => {
|
|
32
|
+
const input = `
|
|
33
|
+
FROM nginx
|
|
34
|
+
WORKDIR /app
|
|
35
|
+
ARG PROJECT_DIR=/
|
|
36
|
+
ARG NGINX_CONF=nginx.conf
|
|
37
|
+
COPY $NGINX_CONF /etc/nginx/conf.d/nginx.conf
|
|
38
|
+
COPY $PROJECT_DIR /app
|
|
39
|
+
CMD mkdir --parents /var/log/nginx && nginx -g "daemon off;"
|
|
40
|
+
`.trim();
|
|
41
|
+
const result = await formatDockerfileContents(input, {
|
|
42
|
+
indent: 4,
|
|
43
|
+
spaceRedirects: false,
|
|
44
|
+
trailingNewline: true,
|
|
45
|
+
});
|
|
46
|
+
assert.ok(result.includes('FROM nginx'));
|
|
47
|
+
assert.ok(result.includes('WORKDIR /app'));
|
|
48
|
+
assert.ok(result.endsWith('\n'));
|
|
49
|
+
});
|
|
50
|
+
it('respects trailingNewline: false', async () => {
|
|
51
|
+
const input = 'FROM alpine';
|
|
52
|
+
const result = await formatDockerfileContents(input, {
|
|
53
|
+
...defaultOptions,
|
|
54
|
+
trailingNewline: false,
|
|
55
|
+
});
|
|
56
|
+
assert.ok(!result.endsWith('\n'));
|
|
57
|
+
});
|
|
58
|
+
it('respects indent option', async () => {
|
|
59
|
+
const input = `FROM alpine
|
|
60
|
+
RUN echo a \\
|
|
61
|
+
&& echo b
|
|
62
|
+
`.trim();
|
|
63
|
+
const result = await formatDockerfileContents(input, {
|
|
64
|
+
...defaultOptions,
|
|
65
|
+
indent: 2,
|
|
66
|
+
});
|
|
67
|
+
assert.ok(result.includes(' && echo b'));
|
|
68
|
+
});
|
|
69
|
+
});
|
package/dist/format.wasm
CHANGED
|
Binary file
|
package/dist/wasm_exec.js
CHANGED
|
@@ -1,54 +1,26 @@
|
|
|
1
|
-
// https://github.com/tinygo-org/tinygo/blob/64d8a043084cb9a56763192000e40ddc5dce733f/targets/wasm_exec.js
|
|
2
1
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
3
2
|
// Use of this source code is governed by a BSD-style
|
|
4
3
|
// license that can be found in the LICENSE file.
|
|
5
|
-
//
|
|
6
|
-
// This file has been modified for use by the TinyGo compiler.
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
// Map multiple JavaScript environments to a single common API,
|
|
10
|
-
// preferring web standards over Node.js API.
|
|
11
|
-
//
|
|
12
|
-
// Environments considered:
|
|
13
|
-
// - Browsers
|
|
14
|
-
// - Node.js
|
|
15
|
-
// - Electron
|
|
16
|
-
// - Parcel
|
|
17
|
-
|
|
18
|
-
if (typeof global !== "undefined") {
|
|
19
|
-
// global already exists
|
|
20
|
-
} else if (typeof window !== "undefined") {
|
|
21
|
-
window.global = window;
|
|
22
|
-
} else if (typeof self !== "undefined") {
|
|
23
|
-
self.global = self;
|
|
24
|
-
} else {
|
|
25
|
-
throw new Error("cannot export Go (neither global, window nor self is defined)");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!global.require && typeof require !== "undefined") {
|
|
29
|
-
global.require = require;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!global.fs && global.require) {
|
|
33
|
-
global.fs = require("node:fs");
|
|
34
|
-
}
|
|
5
|
+
"use strict";
|
|
35
6
|
|
|
7
|
+
(() => {
|
|
36
8
|
const enosys = () => {
|
|
37
9
|
const err = new Error("not implemented");
|
|
38
10
|
err.code = "ENOSYS";
|
|
39
11
|
return err;
|
|
40
12
|
};
|
|
41
13
|
|
|
42
|
-
if (!
|
|
14
|
+
if (!globalThis.fs) {
|
|
43
15
|
let outputBuf = "";
|
|
44
|
-
|
|
45
|
-
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
|
|
16
|
+
globalThis.fs = {
|
|
17
|
+
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused
|
|
46
18
|
writeSync(fd, buf) {
|
|
47
19
|
outputBuf += decoder.decode(buf);
|
|
48
20
|
const nl = outputBuf.lastIndexOf("\n");
|
|
49
21
|
if (nl != -1) {
|
|
50
|
-
console.log(outputBuf.
|
|
51
|
-
outputBuf = outputBuf.
|
|
22
|
+
console.log(outputBuf.substring(0, nl));
|
|
23
|
+
outputBuf = outputBuf.substring(nl + 1);
|
|
52
24
|
}
|
|
53
25
|
return buf.length;
|
|
54
26
|
},
|
|
@@ -86,8 +58,8 @@
|
|
|
86
58
|
};
|
|
87
59
|
}
|
|
88
60
|
|
|
89
|
-
if (!
|
|
90
|
-
|
|
61
|
+
if (!globalThis.process) {
|
|
62
|
+
globalThis.process = {
|
|
91
63
|
getuid() { return -1; },
|
|
92
64
|
getgid() { return -1; },
|
|
93
65
|
geteuid() { return -1; },
|
|
@@ -101,53 +73,70 @@
|
|
|
101
73
|
}
|
|
102
74
|
}
|
|
103
75
|
|
|
104
|
-
if (!
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
76
|
+
if (!globalThis.path) {
|
|
77
|
+
globalThis.path = {
|
|
78
|
+
resolve(...pathSegments) {
|
|
79
|
+
return pathSegments.join("/");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
111
82
|
}
|
|
112
83
|
|
|
113
|
-
if (!
|
|
114
|
-
|
|
115
|
-
now() {
|
|
116
|
-
const [sec, nsec] = process.hrtime();
|
|
117
|
-
return sec * 1000 + nsec / 1000000;
|
|
118
|
-
},
|
|
119
|
-
};
|
|
84
|
+
if (!globalThis.crypto) {
|
|
85
|
+
throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
|
|
120
86
|
}
|
|
121
87
|
|
|
122
|
-
if (!
|
|
123
|
-
|
|
88
|
+
if (!globalThis.performance) {
|
|
89
|
+
throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
|
|
124
90
|
}
|
|
125
91
|
|
|
126
|
-
if (!
|
|
127
|
-
|
|
92
|
+
if (!globalThis.TextEncoder) {
|
|
93
|
+
throw new Error("globalThis.TextEncoder is not available, polyfill required");
|
|
128
94
|
}
|
|
129
95
|
|
|
130
|
-
|
|
96
|
+
if (!globalThis.TextDecoder) {
|
|
97
|
+
throw new Error("globalThis.TextDecoder is not available, polyfill required");
|
|
98
|
+
}
|
|
131
99
|
|
|
132
100
|
const encoder = new TextEncoder("utf-8");
|
|
133
101
|
const decoder = new TextDecoder("utf-8");
|
|
134
|
-
let reinterpretBuf = new DataView(new ArrayBuffer(8));
|
|
135
|
-
var logLine = [];
|
|
136
|
-
const wasmExit = {}; // thrown to exit via proc_exit (not an error)
|
|
137
102
|
|
|
138
|
-
|
|
103
|
+
// Save any existing Go constructor so we can restore it after capturing our own.
|
|
104
|
+
// This prevents clobbering the global Go when other packages (e.g. sh-syntax)
|
|
105
|
+
// rely on their own wasm_exec.js version with different capabilities (like WASI support).
|
|
106
|
+
const __previousGo = globalThis.Go;
|
|
107
|
+
globalThis.Go = class {
|
|
139
108
|
constructor() {
|
|
140
|
-
this.
|
|
109
|
+
this.argv = ["js"];
|
|
110
|
+
this.env = {};
|
|
111
|
+
this.exit = (code) => {
|
|
112
|
+
if (code !== 0) {
|
|
113
|
+
console.warn("exit code:", code);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
this._exitPromise = new Promise((resolve) => {
|
|
117
|
+
this._resolveExitPromise = resolve;
|
|
118
|
+
});
|
|
119
|
+
this._pendingEvent = null;
|
|
120
|
+
this._scheduledTimeouts = new Map();
|
|
141
121
|
this._nextCallbackTimeoutID = 1;
|
|
142
122
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
123
|
+
const setInt64 = (addr, v) => {
|
|
124
|
+
this.mem.setUint32(addr + 0, v, true);
|
|
125
|
+
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
|
|
146
126
|
}
|
|
147
127
|
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
128
|
+
const setInt32 = (addr, v) => {
|
|
129
|
+
this.mem.setUint32(addr + 0, v, true);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const getInt64 = (addr) => {
|
|
133
|
+
const low = this.mem.getUint32(addr + 0, true);
|
|
134
|
+
const high = this.mem.getInt32(addr + 4, true);
|
|
135
|
+
return low + high * 4294967296;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const loadValue = (addr) => {
|
|
139
|
+
const f = this.mem.getFloat64(addr, true);
|
|
151
140
|
if (f === 0) {
|
|
152
141
|
return undefined;
|
|
153
142
|
}
|
|
@@ -155,77 +144,69 @@
|
|
|
155
144
|
return f;
|
|
156
145
|
}
|
|
157
146
|
|
|
158
|
-
const id =
|
|
147
|
+
const id = this.mem.getUint32(addr, true);
|
|
159
148
|
return this._values[id];
|
|
160
149
|
}
|
|
161
150
|
|
|
151
|
+
const storeValue = (addr, v) => {
|
|
152
|
+
const nanHead = 0x7FF80000;
|
|
162
153
|
|
|
163
|
-
|
|
164
|
-
let v_ref = mem().getBigUint64(addr, true);
|
|
165
|
-
return unboxValue(v_ref);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const boxValue = (v) => {
|
|
169
|
-
const nanHead = 0x7FF80000n;
|
|
170
|
-
|
|
171
|
-
if (typeof v === "number") {
|
|
154
|
+
if (typeof v === "number" && v !== 0) {
|
|
172
155
|
if (isNaN(v)) {
|
|
173
|
-
|
|
156
|
+
this.mem.setUint32(addr + 4, nanHead, true);
|
|
157
|
+
this.mem.setUint32(addr, 0, true);
|
|
158
|
+
return;
|
|
174
159
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
reinterpretBuf.setFloat64(0, v, true);
|
|
179
|
-
return reinterpretBuf.getBigInt64(0, true);
|
|
160
|
+
this.mem.setFloat64(addr, v, true);
|
|
161
|
+
return;
|
|
180
162
|
}
|
|
181
163
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
case null:
|
|
186
|
-
return (nanHead << 32n) | 2n;
|
|
187
|
-
case true:
|
|
188
|
-
return (nanHead << 32n) | 3n;
|
|
189
|
-
case false:
|
|
190
|
-
return (nanHead << 32n) | 4n;
|
|
164
|
+
if (v === undefined) {
|
|
165
|
+
this.mem.setFloat64(addr, 0, true);
|
|
166
|
+
return;
|
|
191
167
|
}
|
|
192
168
|
|
|
193
169
|
let id = this._ids.get(v);
|
|
194
170
|
if (id === undefined) {
|
|
195
171
|
id = this._idPool.pop();
|
|
196
172
|
if (id === undefined) {
|
|
197
|
-
id =
|
|
173
|
+
id = this._values.length;
|
|
198
174
|
}
|
|
199
175
|
this._values[id] = v;
|
|
200
176
|
this._goRefCounts[id] = 0;
|
|
201
177
|
this._ids.set(v, id);
|
|
202
178
|
}
|
|
203
179
|
this._goRefCounts[id]++;
|
|
204
|
-
let typeFlag =
|
|
180
|
+
let typeFlag = 0;
|
|
205
181
|
switch (typeof v) {
|
|
182
|
+
case "object":
|
|
183
|
+
if (v !== null) {
|
|
184
|
+
typeFlag = 1;
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
206
187
|
case "string":
|
|
207
|
-
typeFlag =
|
|
188
|
+
typeFlag = 2;
|
|
208
189
|
break;
|
|
209
190
|
case "symbol":
|
|
210
|
-
typeFlag =
|
|
191
|
+
typeFlag = 3;
|
|
211
192
|
break;
|
|
212
193
|
case "function":
|
|
213
|
-
typeFlag =
|
|
194
|
+
typeFlag = 4;
|
|
214
195
|
break;
|
|
215
196
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const storeValue = (addr, v) => {
|
|
220
|
-
let v_ref = boxValue(v);
|
|
221
|
-
mem().setBigUint64(addr, v_ref, true);
|
|
197
|
+
this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
|
|
198
|
+
this.mem.setUint32(addr, id, true);
|
|
222
199
|
}
|
|
223
200
|
|
|
224
|
-
const loadSlice = (
|
|
225
|
-
|
|
201
|
+
const loadSlice = (addr) => {
|
|
202
|
+
const array = getInt64(addr + 0);
|
|
203
|
+
const len = getInt64(addr + 8);
|
|
204
|
+
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
|
|
226
205
|
}
|
|
227
206
|
|
|
228
|
-
const loadSliceOfValues = (
|
|
207
|
+
const loadSliceOfValues = (addr) => {
|
|
208
|
+
const array = getInt64(addr + 0);
|
|
209
|
+
const len = getInt64(addr + 8);
|
|
229
210
|
const a = new Array(len);
|
|
230
211
|
for (let i = 0; i < len; i++) {
|
|
231
212
|
a[i] = loadValue(array + i * 8);
|
|
@@ -233,287 +214,353 @@
|
|
|
233
214
|
return a;
|
|
234
215
|
}
|
|
235
216
|
|
|
236
|
-
const loadString = (
|
|
237
|
-
|
|
217
|
+
const loadString = (addr) => {
|
|
218
|
+
const saddr = getInt64(addr + 0);
|
|
219
|
+
const len = getInt64(addr + 8);
|
|
220
|
+
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const testCallExport = (a, b) => {
|
|
224
|
+
this._inst.exports.testExport0();
|
|
225
|
+
return this._inst.exports.testExport(a, b);
|
|
238
226
|
}
|
|
239
227
|
|
|
240
228
|
const timeOrigin = Date.now() - performance.now();
|
|
241
229
|
this.importObject = {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
} else if (c == 10) { // LF
|
|
257
|
-
// write line
|
|
258
|
-
let line = decoder.decode(new Uint8Array(logLine));
|
|
259
|
-
logLine = [];
|
|
260
|
-
console.log(line);
|
|
261
|
-
} else {
|
|
262
|
-
logLine.push(c);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
console.error('invalid file descriptor:', fd);
|
|
268
|
-
}
|
|
269
|
-
mem().setUint32(nwritten_ptr, nwritten, true);
|
|
270
|
-
return 0;
|
|
271
|
-
},
|
|
272
|
-
fd_close: () => 0, // dummy
|
|
273
|
-
fd_fdstat_get: () => 0, // dummy
|
|
274
|
-
fd_seek: () => 0, // dummy
|
|
275
|
-
proc_exit: (code) => {
|
|
230
|
+
_gotest: {
|
|
231
|
+
add: (a, b) => a + b,
|
|
232
|
+
callExport: testCallExport,
|
|
233
|
+
},
|
|
234
|
+
gojs: {
|
|
235
|
+
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
|
|
236
|
+
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
|
|
237
|
+
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
|
|
238
|
+
// This changes the SP, thus we have to update the SP used by the imported function.
|
|
239
|
+
|
|
240
|
+
// func wasmExit(code int32)
|
|
241
|
+
"runtime.wasmExit": (sp) => {
|
|
242
|
+
sp >>>= 0;
|
|
243
|
+
const code = this.mem.getInt32(sp + 8, true);
|
|
276
244
|
this.exited = true;
|
|
277
|
-
this.
|
|
278
|
-
this.
|
|
279
|
-
|
|
245
|
+
delete this._inst;
|
|
246
|
+
delete this._values;
|
|
247
|
+
delete this._goRefCounts;
|
|
248
|
+
delete this._ids;
|
|
249
|
+
delete this._idPool;
|
|
250
|
+
this.exit(code);
|
|
280
251
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
252
|
+
|
|
253
|
+
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
|
|
254
|
+
"runtime.wasmWrite": (sp) => {
|
|
255
|
+
sp >>>= 0;
|
|
256
|
+
const fd = getInt64(sp + 8);
|
|
257
|
+
const p = getInt64(sp + 16);
|
|
258
|
+
const n = this.mem.getInt32(sp + 24, true);
|
|
259
|
+
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
|
|
284
260
|
},
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
261
|
+
|
|
262
|
+
// func resetMemoryDataView()
|
|
263
|
+
"runtime.resetMemoryDataView": (sp) => {
|
|
264
|
+
sp >>>= 0;
|
|
265
|
+
this.mem = new DataView(this._inst.exports.mem.buffer);
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
// func nanotime1() int64
|
|
269
|
+
"runtime.nanotime1": (sp) => {
|
|
270
|
+
sp >>>= 0;
|
|
271
|
+
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// func walltime() (sec int64, nsec int32)
|
|
275
|
+
"runtime.walltime": (sp) => {
|
|
276
|
+
sp >>>= 0;
|
|
277
|
+
const msec = (new Date).getTime();
|
|
278
|
+
setInt64(sp + 8, msec / 1000);
|
|
279
|
+
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
// func scheduleTimeoutEvent(delay int64) int32
|
|
283
|
+
"runtime.scheduleTimeoutEvent": (sp) => {
|
|
284
|
+
sp >>>= 0;
|
|
285
|
+
const id = this._nextCallbackTimeoutID;
|
|
286
|
+
this._nextCallbackTimeoutID++;
|
|
287
|
+
this._scheduledTimeouts.set(id, setTimeout(
|
|
288
|
+
() => {
|
|
289
|
+
this._resume();
|
|
290
|
+
while (this._scheduledTimeouts.has(id)) {
|
|
291
|
+
// for some reason Go failed to register the timeout event, log and try again
|
|
292
|
+
// (temporary workaround for https://github.com/golang/go/issues/28975)
|
|
293
|
+
console.warn("scheduleTimeoutEvent: missed timeout event");
|
|
294
|
+
this._resume();
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
getInt64(sp + 8),
|
|
298
|
+
));
|
|
299
|
+
this.mem.setInt32(sp + 16, id, true);
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
// func clearTimeoutEvent(id int32)
|
|
303
|
+
"runtime.clearTimeoutEvent": (sp) => {
|
|
304
|
+
sp >>>= 0;
|
|
305
|
+
const id = this.mem.getInt32(sp + 8, true);
|
|
306
|
+
clearTimeout(this._scheduledTimeouts.get(id));
|
|
307
|
+
this._scheduledTimeouts.delete(id);
|
|
290
308
|
},
|
|
291
309
|
|
|
292
|
-
// func
|
|
293
|
-
"runtime.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (this.exited) return;
|
|
297
|
-
try {
|
|
298
|
-
this._inst.exports.go_scheduler();
|
|
299
|
-
} catch (e) {
|
|
300
|
-
if (e !== wasmExit) throw e;
|
|
301
|
-
}
|
|
302
|
-
}, timeout);
|
|
310
|
+
// func getRandomData(r []byte)
|
|
311
|
+
"runtime.getRandomData": (sp) => {
|
|
312
|
+
sp >>>= 0;
|
|
313
|
+
crypto.getRandomValues(loadSlice(sp + 8));
|
|
303
314
|
},
|
|
304
315
|
|
|
305
316
|
// func finalizeRef(v ref)
|
|
306
|
-
"syscall/js.finalizeRef": (
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (this._goRefCounts
|
|
311
|
-
this.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
this._ids.delete(v);
|
|
316
|
-
this._idPool.push(id);
|
|
317
|
-
}
|
|
318
|
-
} else {
|
|
319
|
-
console.error("syscall/js.finalizeRef: unknown id", id);
|
|
317
|
+
"syscall/js.finalizeRef": (sp) => {
|
|
318
|
+
sp >>>= 0;
|
|
319
|
+
const id = this.mem.getUint32(sp + 8, true);
|
|
320
|
+
this._goRefCounts[id]--;
|
|
321
|
+
if (this._goRefCounts[id] === 0) {
|
|
322
|
+
const v = this._values[id];
|
|
323
|
+
this._values[id] = null;
|
|
324
|
+
this._ids.delete(v);
|
|
325
|
+
this._idPool.push(id);
|
|
320
326
|
}
|
|
321
327
|
},
|
|
322
328
|
|
|
323
329
|
// func stringVal(value string) ref
|
|
324
|
-
"syscall/js.stringVal": (
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return boxValue(s);
|
|
330
|
+
"syscall/js.stringVal": (sp) => {
|
|
331
|
+
sp >>>= 0;
|
|
332
|
+
storeValue(sp + 24, loadString(sp + 8));
|
|
328
333
|
},
|
|
329
334
|
|
|
330
335
|
// func valueGet(v ref, p string) ref
|
|
331
|
-
"syscall/js.valueGet": (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
+
"syscall/js.valueGet": (sp) => {
|
|
337
|
+
sp >>>= 0;
|
|
338
|
+
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
|
|
339
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
340
|
+
storeValue(sp + 32, result);
|
|
336
341
|
},
|
|
337
342
|
|
|
338
343
|
// func valueSet(v ref, p string, x ref)
|
|
339
|
-
"syscall/js.valueSet": (
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const x = unboxValue(x_ref);
|
|
343
|
-
Reflect.set(v, p, x);
|
|
344
|
+
"syscall/js.valueSet": (sp) => {
|
|
345
|
+
sp >>>= 0;
|
|
346
|
+
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
|
|
344
347
|
},
|
|
345
348
|
|
|
346
349
|
// func valueDelete(v ref, p string)
|
|
347
|
-
"syscall/js.valueDelete": (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
Reflect.deleteProperty(v, p);
|
|
350
|
+
"syscall/js.valueDelete": (sp) => {
|
|
351
|
+
sp >>>= 0;
|
|
352
|
+
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
|
|
351
353
|
},
|
|
352
354
|
|
|
353
355
|
// func valueIndex(v ref, i int) ref
|
|
354
|
-
"syscall/js.valueIndex": (
|
|
355
|
-
|
|
356
|
+
"syscall/js.valueIndex": (sp) => {
|
|
357
|
+
sp >>>= 0;
|
|
358
|
+
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
|
356
359
|
},
|
|
357
360
|
|
|
358
361
|
// valueSetIndex(v ref, i int, x ref)
|
|
359
|
-
"syscall/js.valueSetIndex": (
|
|
360
|
-
|
|
362
|
+
"syscall/js.valueSetIndex": (sp) => {
|
|
363
|
+
sp >>>= 0;
|
|
364
|
+
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
|
|
361
365
|
},
|
|
362
366
|
|
|
363
367
|
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
|
364
|
-
"syscall/js.valueCall": (
|
|
365
|
-
|
|
366
|
-
const name = loadString(m_ptr, m_len);
|
|
367
|
-
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
|
368
|
+
"syscall/js.valueCall": (sp) => {
|
|
369
|
+
sp >>>= 0;
|
|
368
370
|
try {
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
371
|
+
const v = loadValue(sp + 8);
|
|
372
|
+
const m = Reflect.get(v, loadString(sp + 16));
|
|
373
|
+
const args = loadSliceOfValues(sp + 32);
|
|
374
|
+
const result = Reflect.apply(m, v, args);
|
|
375
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
376
|
+
storeValue(sp + 56, result);
|
|
377
|
+
this.mem.setUint8(sp + 64, 1);
|
|
372
378
|
} catch (err) {
|
|
373
|
-
|
|
374
|
-
|
|
379
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
380
|
+
storeValue(sp + 56, err);
|
|
381
|
+
this.mem.setUint8(sp + 64, 0);
|
|
375
382
|
}
|
|
376
383
|
},
|
|
377
384
|
|
|
378
385
|
// func valueInvoke(v ref, args []ref) (ref, bool)
|
|
379
|
-
"syscall/js.valueInvoke": (
|
|
386
|
+
"syscall/js.valueInvoke": (sp) => {
|
|
387
|
+
sp >>>= 0;
|
|
380
388
|
try {
|
|
381
|
-
const v =
|
|
382
|
-
const args = loadSliceOfValues(
|
|
383
|
-
|
|
384
|
-
|
|
389
|
+
const v = loadValue(sp + 8);
|
|
390
|
+
const args = loadSliceOfValues(sp + 16);
|
|
391
|
+
const result = Reflect.apply(v, undefined, args);
|
|
392
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
393
|
+
storeValue(sp + 40, result);
|
|
394
|
+
this.mem.setUint8(sp + 48, 1);
|
|
385
395
|
} catch (err) {
|
|
386
|
-
|
|
387
|
-
|
|
396
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
397
|
+
storeValue(sp + 40, err);
|
|
398
|
+
this.mem.setUint8(sp + 48, 0);
|
|
388
399
|
}
|
|
389
400
|
},
|
|
390
401
|
|
|
391
402
|
// func valueNew(v ref, args []ref) (ref, bool)
|
|
392
|
-
"syscall/js.valueNew": (
|
|
393
|
-
|
|
394
|
-
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
|
403
|
+
"syscall/js.valueNew": (sp) => {
|
|
404
|
+
sp >>>= 0;
|
|
395
405
|
try {
|
|
396
|
-
|
|
397
|
-
|
|
406
|
+
const v = loadValue(sp + 8);
|
|
407
|
+
const args = loadSliceOfValues(sp + 16);
|
|
408
|
+
const result = Reflect.construct(v, args);
|
|
409
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
410
|
+
storeValue(sp + 40, result);
|
|
411
|
+
this.mem.setUint8(sp + 48, 1);
|
|
398
412
|
} catch (err) {
|
|
399
|
-
|
|
400
|
-
|
|
413
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
414
|
+
storeValue(sp + 40, err);
|
|
415
|
+
this.mem.setUint8(sp + 48, 0);
|
|
401
416
|
}
|
|
402
417
|
},
|
|
403
418
|
|
|
404
419
|
// func valueLength(v ref) int
|
|
405
|
-
"syscall/js.valueLength": (
|
|
406
|
-
|
|
420
|
+
"syscall/js.valueLength": (sp) => {
|
|
421
|
+
sp >>>= 0;
|
|
422
|
+
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
|
407
423
|
},
|
|
408
424
|
|
|
409
425
|
// valuePrepareString(v ref) (ref, int)
|
|
410
|
-
"syscall/js.valuePrepareString": (
|
|
411
|
-
|
|
412
|
-
const str = encoder.encode(
|
|
413
|
-
storeValue(
|
|
414
|
-
|
|
426
|
+
"syscall/js.valuePrepareString": (sp) => {
|
|
427
|
+
sp >>>= 0;
|
|
428
|
+
const str = encoder.encode(String(loadValue(sp + 8)));
|
|
429
|
+
storeValue(sp + 16, str);
|
|
430
|
+
setInt64(sp + 24, str.length);
|
|
415
431
|
},
|
|
416
432
|
|
|
417
433
|
// valueLoadString(v ref, b []byte)
|
|
418
|
-
"syscall/js.valueLoadString": (
|
|
419
|
-
|
|
420
|
-
|
|
434
|
+
"syscall/js.valueLoadString": (sp) => {
|
|
435
|
+
sp >>>= 0;
|
|
436
|
+
const str = loadValue(sp + 8);
|
|
437
|
+
loadSlice(sp + 16).set(str);
|
|
421
438
|
},
|
|
422
439
|
|
|
423
440
|
// func valueInstanceOf(v ref, t ref) bool
|
|
424
|
-
"syscall/js.valueInstanceOf": (
|
|
425
|
-
|
|
441
|
+
"syscall/js.valueInstanceOf": (sp) => {
|
|
442
|
+
sp >>>= 0;
|
|
443
|
+
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
|
|
426
444
|
},
|
|
427
445
|
|
|
428
446
|
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
|
429
|
-
"syscall/js.copyBytesToGo": (
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const dst = loadSlice(dest_addr, dest_len);
|
|
434
|
-
const src = unboxValue(src_ref);
|
|
447
|
+
"syscall/js.copyBytesToGo": (sp) => {
|
|
448
|
+
sp >>>= 0;
|
|
449
|
+
const dst = loadSlice(sp + 8);
|
|
450
|
+
const src = loadValue(sp + 32);
|
|
435
451
|
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
|
|
436
|
-
mem
|
|
452
|
+
this.mem.setUint8(sp + 48, 0);
|
|
437
453
|
return;
|
|
438
454
|
}
|
|
439
455
|
const toCopy = src.subarray(0, dst.length);
|
|
440
456
|
dst.set(toCopy);
|
|
441
|
-
|
|
442
|
-
mem
|
|
457
|
+
setInt64(sp + 40, toCopy.length);
|
|
458
|
+
this.mem.setUint8(sp + 48, 1);
|
|
443
459
|
},
|
|
444
460
|
|
|
445
|
-
// copyBytesToJS(dst ref, src []byte) (int, bool)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
|
|
451
|
-
|
|
452
|
-
const dst = unboxValue(dst_ref);
|
|
453
|
-
const src = loadSlice(src_addr, src_len);
|
|
461
|
+
// func copyBytesToJS(dst ref, src []byte) (int, bool)
|
|
462
|
+
"syscall/js.copyBytesToJS": (sp) => {
|
|
463
|
+
sp >>>= 0;
|
|
464
|
+
const dst = loadValue(sp + 8);
|
|
465
|
+
const src = loadSlice(sp + 16);
|
|
454
466
|
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
|
|
455
|
-
mem
|
|
467
|
+
this.mem.setUint8(sp + 48, 0);
|
|
456
468
|
return;
|
|
457
469
|
}
|
|
458
470
|
const toCopy = src.subarray(0, dst.length);
|
|
459
471
|
dst.set(toCopy);
|
|
460
|
-
|
|
461
|
-
mem
|
|
472
|
+
setInt64(sp + 40, toCopy.length);
|
|
473
|
+
this.mem.setUint8(sp + 48, 1);
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
"debug": (value) => {
|
|
477
|
+
console.log(value);
|
|
462
478
|
},
|
|
463
479
|
}
|
|
464
480
|
};
|
|
465
|
-
|
|
466
|
-
// Go 1.20 uses 'env'. Go 1.21 uses 'gojs'.
|
|
467
|
-
// For compatibility, we use both as long as Go 1.20 is supported.
|
|
468
|
-
this.importObject.env = this.importObject.gojs;
|
|
469
481
|
}
|
|
470
482
|
|
|
471
483
|
async run(instance) {
|
|
484
|
+
if (!(instance instanceof WebAssembly.Instance)) {
|
|
485
|
+
throw new Error("Go.run: WebAssembly.Instance expected");
|
|
486
|
+
}
|
|
472
487
|
this._inst = instance;
|
|
488
|
+
this.mem = new DataView(this._inst.exports.mem.buffer);
|
|
473
489
|
this._values = [ // JS values that Go currently has references to, indexed by reference id
|
|
474
490
|
NaN,
|
|
475
491
|
0,
|
|
476
492
|
null,
|
|
477
493
|
true,
|
|
478
494
|
false,
|
|
479
|
-
|
|
495
|
+
globalThis,
|
|
480
496
|
this,
|
|
481
497
|
];
|
|
482
|
-
this._goRefCounts =
|
|
483
|
-
this._ids = new Map(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
498
|
+
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
|
|
499
|
+
this._ids = new Map([ // mapping from JS values to reference ids
|
|
500
|
+
[0, 1],
|
|
501
|
+
[null, 2],
|
|
502
|
+
[true, 3],
|
|
503
|
+
[false, 4],
|
|
504
|
+
[globalThis, 5],
|
|
505
|
+
[this, 6],
|
|
506
|
+
]);
|
|
507
|
+
this._idPool = []; // unused ids that have been garbage collected
|
|
508
|
+
this.exited = false; // whether the Go program has exited
|
|
509
|
+
|
|
510
|
+
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
|
|
511
|
+
let offset = 4096;
|
|
512
|
+
|
|
513
|
+
const strPtr = (str) => {
|
|
514
|
+
const ptr = offset;
|
|
515
|
+
const bytes = encoder.encode(str + "\0");
|
|
516
|
+
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
|
|
517
|
+
offset += bytes.length;
|
|
518
|
+
if (offset % 8 !== 0) {
|
|
519
|
+
offset += 8 - (offset % 8);
|
|
499
520
|
}
|
|
521
|
+
return ptr;
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const argc = this.argv.length;
|
|
525
|
+
|
|
526
|
+
const argvPtrs = [];
|
|
527
|
+
this.argv.forEach((arg) => {
|
|
528
|
+
argvPtrs.push(strPtr(arg));
|
|
529
|
+
});
|
|
530
|
+
argvPtrs.push(0);
|
|
531
|
+
|
|
532
|
+
const keys = Object.keys(this.env).sort();
|
|
533
|
+
keys.forEach((key) => {
|
|
534
|
+
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
|
|
535
|
+
});
|
|
536
|
+
argvPtrs.push(0);
|
|
537
|
+
|
|
538
|
+
const argv = offset;
|
|
539
|
+
argvPtrs.forEach((ptr) => {
|
|
540
|
+
this.mem.setUint32(offset, ptr, true);
|
|
541
|
+
this.mem.setUint32(offset + 4, 0, true);
|
|
542
|
+
offset += 8;
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// The linker guarantees global data starts from at least wasmMinDataAddr.
|
|
546
|
+
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
|
|
547
|
+
const wasmMinDataAddr = 4096 + 8192;
|
|
548
|
+
if (offset >= wasmMinDataAddr) {
|
|
549
|
+
throw new Error("total length of command line and environment variables exceeds limit");
|
|
550
|
+
}
|
|
500
551
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this._inst.exports._initialize();
|
|
552
|
+
this._inst.exports.run(argc, argv);
|
|
553
|
+
if (this.exited) {
|
|
554
|
+
this._resolveExitPromise();
|
|
505
555
|
}
|
|
556
|
+
await this._exitPromise;
|
|
506
557
|
}
|
|
507
558
|
|
|
508
559
|
_resume() {
|
|
509
560
|
if (this.exited) {
|
|
510
561
|
throw new Error("Go program has already exited");
|
|
511
562
|
}
|
|
512
|
-
|
|
513
|
-
this._inst.exports.resume();
|
|
514
|
-
} catch (e) {
|
|
515
|
-
if (e !== wasmExit) throw e;
|
|
516
|
-
}
|
|
563
|
+
this._inst.exports.resume();
|
|
517
564
|
if (this.exited) {
|
|
518
565
|
this._resolveExitPromise();
|
|
519
566
|
}
|
|
@@ -529,4 +576,12 @@
|
|
|
529
576
|
};
|
|
530
577
|
}
|
|
531
578
|
}
|
|
579
|
+
// Stash our Go class under a namespaced global and restore the previous one,
|
|
580
|
+
// so we don't break other Go WASM packages sharing the same global scope.
|
|
581
|
+
globalThis.__dockerfmt_Go = globalThis.Go;
|
|
582
|
+
if (__previousGo) {
|
|
583
|
+
globalThis.Go = __previousGo;
|
|
584
|
+
} else {
|
|
585
|
+
delete globalThis.Go;
|
|
586
|
+
}
|
|
532
587
|
})();
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reteps/dockerfmt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
|
-
"repository":
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/reteps/dockerfmt.git",
|
|
9
|
+
"directory": "js"
|
|
10
|
+
},
|
|
7
11
|
"author": "Peter Stenger <pete@stenger.io>",
|
|
8
12
|
"license": "MIT",
|
|
9
13
|
"engines": {
|
|
@@ -29,9 +33,8 @@
|
|
|
29
33
|
"dist"
|
|
30
34
|
],
|
|
31
35
|
"scripts": {
|
|
32
|
-
"//": "Requires tinygo 0.38.0 or later",
|
|
33
36
|
"build": "npm run build-go && npm run build-js",
|
|
34
|
-
"build-go": "
|
|
37
|
+
"build-go": "GOOS=js GOARCH=wasm go build -ldflags='-s -w' -o format.wasm && wasm-opt --enable-bulk-memory -Oz -o format-opt.wasm format.wasm && mv format-opt.wasm format.wasm",
|
|
35
38
|
"build-js": "tsc && cp format.wasm wasm_exec.js dist",
|
|
36
39
|
"format": "prettier --write \"**/*.{js,ts,json}\""
|
|
37
40
|
},
|