@reteps/dockerfmt 0.3.6 → 0.5.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/README.md +1 -1
- package/dist/format.js +6 -16
- 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 +330 -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,19 @@
|
|
|
1
1
|
import './wasm_exec.js';
|
|
2
2
|
export const formatDockerfileContents = async (fileContents, options, getWasm) => {
|
|
3
3
|
const go = new Go(); // Defined in wasm_exec.js
|
|
4
|
-
const encoder = new TextEncoder();
|
|
5
|
-
const decoder = new TextDecoder();
|
|
6
|
-
// get current working directory
|
|
7
4
|
const wasmBuffer = await getWasm();
|
|
8
5
|
const wasm = await WebAssembly.instantiate(wasmBuffer, go.importObject);
|
|
9
6
|
/**
|
|
10
7
|
* Do not await this promise, because it only resolves once the go main()
|
|
11
8
|
* function has exited. But we need the main function to stay alive to be
|
|
12
|
-
* able to call the
|
|
9
|
+
* able to call the formatBytes function.
|
|
13
10
|
*/
|
|
14
11
|
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;
|
|
12
|
+
const formatBytes = globalThis.__dockerfmt_formatBytes;
|
|
13
|
+
if (typeof formatBytes !== 'function') {
|
|
14
|
+
throw new Error('dockerfmt WASM module did not register formatBytes');
|
|
15
|
+
}
|
|
16
|
+
return formatBytes(fileContents, options.indent, options.trailingNewline, options.spaceRedirects);
|
|
27
17
|
};
|
|
28
18
|
export const formatDockerfile = () => {
|
|
29
19
|
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,66 @@
|
|
|
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
|
+
globalThis.Go = class {
|
|
139
104
|
constructor() {
|
|
140
|
-
this.
|
|
105
|
+
this.argv = ["js"];
|
|
106
|
+
this.env = {};
|
|
107
|
+
this.exit = (code) => {
|
|
108
|
+
if (code !== 0) {
|
|
109
|
+
console.warn("exit code:", code);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
this._exitPromise = new Promise((resolve) => {
|
|
113
|
+
this._resolveExitPromise = resolve;
|
|
114
|
+
});
|
|
115
|
+
this._pendingEvent = null;
|
|
116
|
+
this._scheduledTimeouts = new Map();
|
|
141
117
|
this._nextCallbackTimeoutID = 1;
|
|
142
118
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
119
|
+
const setInt64 = (addr, v) => {
|
|
120
|
+
this.mem.setUint32(addr + 0, v, true);
|
|
121
|
+
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
|
|
146
122
|
}
|
|
147
123
|
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
124
|
+
const setInt32 = (addr, v) => {
|
|
125
|
+
this.mem.setUint32(addr + 0, v, true);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const getInt64 = (addr) => {
|
|
129
|
+
const low = this.mem.getUint32(addr + 0, true);
|
|
130
|
+
const high = this.mem.getInt32(addr + 4, true);
|
|
131
|
+
return low + high * 4294967296;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const loadValue = (addr) => {
|
|
135
|
+
const f = this.mem.getFloat64(addr, true);
|
|
151
136
|
if (f === 0) {
|
|
152
137
|
return undefined;
|
|
153
138
|
}
|
|
@@ -155,77 +140,69 @@
|
|
|
155
140
|
return f;
|
|
156
141
|
}
|
|
157
142
|
|
|
158
|
-
const id =
|
|
143
|
+
const id = this.mem.getUint32(addr, true);
|
|
159
144
|
return this._values[id];
|
|
160
145
|
}
|
|
161
146
|
|
|
147
|
+
const storeValue = (addr, v) => {
|
|
148
|
+
const nanHead = 0x7FF80000;
|
|
162
149
|
|
|
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") {
|
|
150
|
+
if (typeof v === "number" && v !== 0) {
|
|
172
151
|
if (isNaN(v)) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return (nanHead << 32n) | 1n;
|
|
152
|
+
this.mem.setUint32(addr + 4, nanHead, true);
|
|
153
|
+
this.mem.setUint32(addr, 0, true);
|
|
154
|
+
return;
|
|
177
155
|
}
|
|
178
|
-
|
|
179
|
-
return
|
|
156
|
+
this.mem.setFloat64(addr, v, true);
|
|
157
|
+
return;
|
|
180
158
|
}
|
|
181
159
|
|
|
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;
|
|
160
|
+
if (v === undefined) {
|
|
161
|
+
this.mem.setFloat64(addr, 0, true);
|
|
162
|
+
return;
|
|
191
163
|
}
|
|
192
164
|
|
|
193
165
|
let id = this._ids.get(v);
|
|
194
166
|
if (id === undefined) {
|
|
195
167
|
id = this._idPool.pop();
|
|
196
168
|
if (id === undefined) {
|
|
197
|
-
id =
|
|
169
|
+
id = this._values.length;
|
|
198
170
|
}
|
|
199
171
|
this._values[id] = v;
|
|
200
172
|
this._goRefCounts[id] = 0;
|
|
201
173
|
this._ids.set(v, id);
|
|
202
174
|
}
|
|
203
175
|
this._goRefCounts[id]++;
|
|
204
|
-
let typeFlag =
|
|
176
|
+
let typeFlag = 0;
|
|
205
177
|
switch (typeof v) {
|
|
178
|
+
case "object":
|
|
179
|
+
if (v !== null) {
|
|
180
|
+
typeFlag = 1;
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
206
183
|
case "string":
|
|
207
|
-
typeFlag =
|
|
184
|
+
typeFlag = 2;
|
|
208
185
|
break;
|
|
209
186
|
case "symbol":
|
|
210
|
-
typeFlag =
|
|
187
|
+
typeFlag = 3;
|
|
211
188
|
break;
|
|
212
189
|
case "function":
|
|
213
|
-
typeFlag =
|
|
190
|
+
typeFlag = 4;
|
|
214
191
|
break;
|
|
215
192
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const storeValue = (addr, v) => {
|
|
220
|
-
let v_ref = boxValue(v);
|
|
221
|
-
mem().setBigUint64(addr, v_ref, true);
|
|
193
|
+
this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
|
|
194
|
+
this.mem.setUint32(addr, id, true);
|
|
222
195
|
}
|
|
223
196
|
|
|
224
|
-
const loadSlice = (
|
|
225
|
-
|
|
197
|
+
const loadSlice = (addr) => {
|
|
198
|
+
const array = getInt64(addr + 0);
|
|
199
|
+
const len = getInt64(addr + 8);
|
|
200
|
+
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
|
|
226
201
|
}
|
|
227
202
|
|
|
228
|
-
const loadSliceOfValues = (
|
|
203
|
+
const loadSliceOfValues = (addr) => {
|
|
204
|
+
const array = getInt64(addr + 0);
|
|
205
|
+
const len = getInt64(addr + 8);
|
|
229
206
|
const a = new Array(len);
|
|
230
207
|
for (let i = 0; i < len; i++) {
|
|
231
208
|
a[i] = loadValue(array + i * 8);
|
|
@@ -233,287 +210,353 @@
|
|
|
233
210
|
return a;
|
|
234
211
|
}
|
|
235
212
|
|
|
236
|
-
const loadString = (
|
|
237
|
-
|
|
213
|
+
const loadString = (addr) => {
|
|
214
|
+
const saddr = getInt64(addr + 0);
|
|
215
|
+
const len = getInt64(addr + 8);
|
|
216
|
+
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const testCallExport = (a, b) => {
|
|
220
|
+
this._inst.exports.testExport0();
|
|
221
|
+
return this._inst.exports.testExport(a, b);
|
|
238
222
|
}
|
|
239
223
|
|
|
240
224
|
const timeOrigin = Date.now() - performance.now();
|
|
241
225
|
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) => {
|
|
226
|
+
_gotest: {
|
|
227
|
+
add: (a, b) => a + b,
|
|
228
|
+
callExport: testCallExport,
|
|
229
|
+
},
|
|
230
|
+
gojs: {
|
|
231
|
+
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
|
|
232
|
+
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
|
|
233
|
+
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
|
|
234
|
+
// This changes the SP, thus we have to update the SP used by the imported function.
|
|
235
|
+
|
|
236
|
+
// func wasmExit(code int32)
|
|
237
|
+
"runtime.wasmExit": (sp) => {
|
|
238
|
+
sp >>>= 0;
|
|
239
|
+
const code = this.mem.getInt32(sp + 8, true);
|
|
276
240
|
this.exited = true;
|
|
277
|
-
this.
|
|
278
|
-
this.
|
|
279
|
-
|
|
241
|
+
delete this._inst;
|
|
242
|
+
delete this._values;
|
|
243
|
+
delete this._goRefCounts;
|
|
244
|
+
delete this._ids;
|
|
245
|
+
delete this._idPool;
|
|
246
|
+
this.exit(code);
|
|
280
247
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
248
|
+
|
|
249
|
+
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
|
|
250
|
+
"runtime.wasmWrite": (sp) => {
|
|
251
|
+
sp >>>= 0;
|
|
252
|
+
const fd = getInt64(sp + 8);
|
|
253
|
+
const p = getInt64(sp + 16);
|
|
254
|
+
const n = this.mem.getInt32(sp + 24, true);
|
|
255
|
+
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
|
|
284
256
|
},
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
257
|
+
|
|
258
|
+
// func resetMemoryDataView()
|
|
259
|
+
"runtime.resetMemoryDataView": (sp) => {
|
|
260
|
+
sp >>>= 0;
|
|
261
|
+
this.mem = new DataView(this._inst.exports.mem.buffer);
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// func nanotime1() int64
|
|
265
|
+
"runtime.nanotime1": (sp) => {
|
|
266
|
+
sp >>>= 0;
|
|
267
|
+
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
// func walltime() (sec int64, nsec int32)
|
|
271
|
+
"runtime.walltime": (sp) => {
|
|
272
|
+
sp >>>= 0;
|
|
273
|
+
const msec = (new Date).getTime();
|
|
274
|
+
setInt64(sp + 8, msec / 1000);
|
|
275
|
+
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
|
|
290
276
|
},
|
|
291
277
|
|
|
292
|
-
// func
|
|
293
|
-
"runtime.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
278
|
+
// func scheduleTimeoutEvent(delay int64) int32
|
|
279
|
+
"runtime.scheduleTimeoutEvent": (sp) => {
|
|
280
|
+
sp >>>= 0;
|
|
281
|
+
const id = this._nextCallbackTimeoutID;
|
|
282
|
+
this._nextCallbackTimeoutID++;
|
|
283
|
+
this._scheduledTimeouts.set(id, setTimeout(
|
|
284
|
+
() => {
|
|
285
|
+
this._resume();
|
|
286
|
+
while (this._scheduledTimeouts.has(id)) {
|
|
287
|
+
// for some reason Go failed to register the timeout event, log and try again
|
|
288
|
+
// (temporary workaround for https://github.com/golang/go/issues/28975)
|
|
289
|
+
console.warn("scheduleTimeoutEvent: missed timeout event");
|
|
290
|
+
this._resume();
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
getInt64(sp + 8),
|
|
294
|
+
));
|
|
295
|
+
this.mem.setInt32(sp + 16, id, true);
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// func clearTimeoutEvent(id int32)
|
|
299
|
+
"runtime.clearTimeoutEvent": (sp) => {
|
|
300
|
+
sp >>>= 0;
|
|
301
|
+
const id = this.mem.getInt32(sp + 8, true);
|
|
302
|
+
clearTimeout(this._scheduledTimeouts.get(id));
|
|
303
|
+
this._scheduledTimeouts.delete(id);
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
// func getRandomData(r []byte)
|
|
307
|
+
"runtime.getRandomData": (sp) => {
|
|
308
|
+
sp >>>= 0;
|
|
309
|
+
crypto.getRandomValues(loadSlice(sp + 8));
|
|
303
310
|
},
|
|
304
311
|
|
|
305
312
|
// 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);
|
|
313
|
+
"syscall/js.finalizeRef": (sp) => {
|
|
314
|
+
sp >>>= 0;
|
|
315
|
+
const id = this.mem.getUint32(sp + 8, true);
|
|
316
|
+
this._goRefCounts[id]--;
|
|
317
|
+
if (this._goRefCounts[id] === 0) {
|
|
318
|
+
const v = this._values[id];
|
|
319
|
+
this._values[id] = null;
|
|
320
|
+
this._ids.delete(v);
|
|
321
|
+
this._idPool.push(id);
|
|
320
322
|
}
|
|
321
323
|
},
|
|
322
324
|
|
|
323
325
|
// func stringVal(value string) ref
|
|
324
|
-
"syscall/js.stringVal": (
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return boxValue(s);
|
|
326
|
+
"syscall/js.stringVal": (sp) => {
|
|
327
|
+
sp >>>= 0;
|
|
328
|
+
storeValue(sp + 24, loadString(sp + 8));
|
|
328
329
|
},
|
|
329
330
|
|
|
330
331
|
// func valueGet(v ref, p string) ref
|
|
331
|
-
"syscall/js.valueGet": (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
332
|
+
"syscall/js.valueGet": (sp) => {
|
|
333
|
+
sp >>>= 0;
|
|
334
|
+
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
|
|
335
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
336
|
+
storeValue(sp + 32, result);
|
|
336
337
|
},
|
|
337
338
|
|
|
338
339
|
// 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);
|
|
340
|
+
"syscall/js.valueSet": (sp) => {
|
|
341
|
+
sp >>>= 0;
|
|
342
|
+
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
|
|
344
343
|
},
|
|
345
344
|
|
|
346
345
|
// func valueDelete(v ref, p string)
|
|
347
|
-
"syscall/js.valueDelete": (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
Reflect.deleteProperty(v, p);
|
|
346
|
+
"syscall/js.valueDelete": (sp) => {
|
|
347
|
+
sp >>>= 0;
|
|
348
|
+
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
|
|
351
349
|
},
|
|
352
350
|
|
|
353
351
|
// func valueIndex(v ref, i int) ref
|
|
354
|
-
"syscall/js.valueIndex": (
|
|
355
|
-
|
|
352
|
+
"syscall/js.valueIndex": (sp) => {
|
|
353
|
+
sp >>>= 0;
|
|
354
|
+
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
|
356
355
|
},
|
|
357
356
|
|
|
358
357
|
// valueSetIndex(v ref, i int, x ref)
|
|
359
|
-
"syscall/js.valueSetIndex": (
|
|
360
|
-
|
|
358
|
+
"syscall/js.valueSetIndex": (sp) => {
|
|
359
|
+
sp >>>= 0;
|
|
360
|
+
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
|
|
361
361
|
},
|
|
362
362
|
|
|
363
363
|
// 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);
|
|
364
|
+
"syscall/js.valueCall": (sp) => {
|
|
365
|
+
sp >>>= 0;
|
|
368
366
|
try {
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
367
|
+
const v = loadValue(sp + 8);
|
|
368
|
+
const m = Reflect.get(v, loadString(sp + 16));
|
|
369
|
+
const args = loadSliceOfValues(sp + 32);
|
|
370
|
+
const result = Reflect.apply(m, v, args);
|
|
371
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
372
|
+
storeValue(sp + 56, result);
|
|
373
|
+
this.mem.setUint8(sp + 64, 1);
|
|
372
374
|
} catch (err) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
376
|
+
storeValue(sp + 56, err);
|
|
377
|
+
this.mem.setUint8(sp + 64, 0);
|
|
375
378
|
}
|
|
376
379
|
},
|
|
377
380
|
|
|
378
381
|
// func valueInvoke(v ref, args []ref) (ref, bool)
|
|
379
|
-
"syscall/js.valueInvoke": (
|
|
382
|
+
"syscall/js.valueInvoke": (sp) => {
|
|
383
|
+
sp >>>= 0;
|
|
380
384
|
try {
|
|
381
|
-
const v =
|
|
382
|
-
const args = loadSliceOfValues(
|
|
383
|
-
|
|
384
|
-
|
|
385
|
+
const v = loadValue(sp + 8);
|
|
386
|
+
const args = loadSliceOfValues(sp + 16);
|
|
387
|
+
const result = Reflect.apply(v, undefined, args);
|
|
388
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
389
|
+
storeValue(sp + 40, result);
|
|
390
|
+
this.mem.setUint8(sp + 48, 1);
|
|
385
391
|
} catch (err) {
|
|
386
|
-
|
|
387
|
-
|
|
392
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
393
|
+
storeValue(sp + 40, err);
|
|
394
|
+
this.mem.setUint8(sp + 48, 0);
|
|
388
395
|
}
|
|
389
396
|
},
|
|
390
397
|
|
|
391
398
|
// func valueNew(v ref, args []ref) (ref, bool)
|
|
392
|
-
"syscall/js.valueNew": (
|
|
393
|
-
|
|
394
|
-
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
|
399
|
+
"syscall/js.valueNew": (sp) => {
|
|
400
|
+
sp >>>= 0;
|
|
395
401
|
try {
|
|
396
|
-
|
|
397
|
-
|
|
402
|
+
const v = loadValue(sp + 8);
|
|
403
|
+
const args = loadSliceOfValues(sp + 16);
|
|
404
|
+
const result = Reflect.construct(v, args);
|
|
405
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
406
|
+
storeValue(sp + 40, result);
|
|
407
|
+
this.mem.setUint8(sp + 48, 1);
|
|
398
408
|
} catch (err) {
|
|
399
|
-
|
|
400
|
-
|
|
409
|
+
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
|
410
|
+
storeValue(sp + 40, err);
|
|
411
|
+
this.mem.setUint8(sp + 48, 0);
|
|
401
412
|
}
|
|
402
413
|
},
|
|
403
414
|
|
|
404
415
|
// func valueLength(v ref) int
|
|
405
|
-
"syscall/js.valueLength": (
|
|
406
|
-
|
|
416
|
+
"syscall/js.valueLength": (sp) => {
|
|
417
|
+
sp >>>= 0;
|
|
418
|
+
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
|
407
419
|
},
|
|
408
420
|
|
|
409
421
|
// valuePrepareString(v ref) (ref, int)
|
|
410
|
-
"syscall/js.valuePrepareString": (
|
|
411
|
-
|
|
412
|
-
const str = encoder.encode(
|
|
413
|
-
storeValue(
|
|
414
|
-
|
|
422
|
+
"syscall/js.valuePrepareString": (sp) => {
|
|
423
|
+
sp >>>= 0;
|
|
424
|
+
const str = encoder.encode(String(loadValue(sp + 8)));
|
|
425
|
+
storeValue(sp + 16, str);
|
|
426
|
+
setInt64(sp + 24, str.length);
|
|
415
427
|
},
|
|
416
428
|
|
|
417
429
|
// valueLoadString(v ref, b []byte)
|
|
418
|
-
"syscall/js.valueLoadString": (
|
|
419
|
-
|
|
420
|
-
|
|
430
|
+
"syscall/js.valueLoadString": (sp) => {
|
|
431
|
+
sp >>>= 0;
|
|
432
|
+
const str = loadValue(sp + 8);
|
|
433
|
+
loadSlice(sp + 16).set(str);
|
|
421
434
|
},
|
|
422
435
|
|
|
423
436
|
// func valueInstanceOf(v ref, t ref) bool
|
|
424
|
-
"syscall/js.valueInstanceOf": (
|
|
425
|
-
|
|
437
|
+
"syscall/js.valueInstanceOf": (sp) => {
|
|
438
|
+
sp >>>= 0;
|
|
439
|
+
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
|
|
426
440
|
},
|
|
427
441
|
|
|
428
442
|
// 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);
|
|
443
|
+
"syscall/js.copyBytesToGo": (sp) => {
|
|
444
|
+
sp >>>= 0;
|
|
445
|
+
const dst = loadSlice(sp + 8);
|
|
446
|
+
const src = loadValue(sp + 32);
|
|
435
447
|
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
|
|
436
|
-
mem
|
|
448
|
+
this.mem.setUint8(sp + 48, 0);
|
|
437
449
|
return;
|
|
438
450
|
}
|
|
439
451
|
const toCopy = src.subarray(0, dst.length);
|
|
440
452
|
dst.set(toCopy);
|
|
441
|
-
|
|
442
|
-
mem
|
|
453
|
+
setInt64(sp + 40, toCopy.length);
|
|
454
|
+
this.mem.setUint8(sp + 48, 1);
|
|
443
455
|
},
|
|
444
456
|
|
|
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);
|
|
457
|
+
// func copyBytesToJS(dst ref, src []byte) (int, bool)
|
|
458
|
+
"syscall/js.copyBytesToJS": (sp) => {
|
|
459
|
+
sp >>>= 0;
|
|
460
|
+
const dst = loadValue(sp + 8);
|
|
461
|
+
const src = loadSlice(sp + 16);
|
|
454
462
|
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
|
|
455
|
-
mem
|
|
463
|
+
this.mem.setUint8(sp + 48, 0);
|
|
456
464
|
return;
|
|
457
465
|
}
|
|
458
466
|
const toCopy = src.subarray(0, dst.length);
|
|
459
467
|
dst.set(toCopy);
|
|
460
|
-
|
|
461
|
-
mem
|
|
468
|
+
setInt64(sp + 40, toCopy.length);
|
|
469
|
+
this.mem.setUint8(sp + 48, 1);
|
|
470
|
+
},
|
|
471
|
+
|
|
472
|
+
"debug": (value) => {
|
|
473
|
+
console.log(value);
|
|
462
474
|
},
|
|
463
475
|
}
|
|
464
476
|
};
|
|
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
477
|
}
|
|
470
478
|
|
|
471
479
|
async run(instance) {
|
|
480
|
+
if (!(instance instanceof WebAssembly.Instance)) {
|
|
481
|
+
throw new Error("Go.run: WebAssembly.Instance expected");
|
|
482
|
+
}
|
|
472
483
|
this._inst = instance;
|
|
484
|
+
this.mem = new DataView(this._inst.exports.mem.buffer);
|
|
473
485
|
this._values = [ // JS values that Go currently has references to, indexed by reference id
|
|
474
486
|
NaN,
|
|
475
487
|
0,
|
|
476
488
|
null,
|
|
477
489
|
true,
|
|
478
490
|
false,
|
|
479
|
-
|
|
491
|
+
globalThis,
|
|
480
492
|
this,
|
|
481
493
|
];
|
|
482
|
-
this._goRefCounts =
|
|
483
|
-
this._ids = new Map(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
494
|
+
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
|
|
495
|
+
this._ids = new Map([ // mapping from JS values to reference ids
|
|
496
|
+
[0, 1],
|
|
497
|
+
[null, 2],
|
|
498
|
+
[true, 3],
|
|
499
|
+
[false, 4],
|
|
500
|
+
[globalThis, 5],
|
|
501
|
+
[this, 6],
|
|
502
|
+
]);
|
|
503
|
+
this._idPool = []; // unused ids that have been garbage collected
|
|
504
|
+
this.exited = false; // whether the Go program has exited
|
|
505
|
+
|
|
506
|
+
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
|
|
507
|
+
let offset = 4096;
|
|
508
|
+
|
|
509
|
+
const strPtr = (str) => {
|
|
510
|
+
const ptr = offset;
|
|
511
|
+
const bytes = encoder.encode(str + "\0");
|
|
512
|
+
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
|
|
513
|
+
offset += bytes.length;
|
|
514
|
+
if (offset % 8 !== 0) {
|
|
515
|
+
offset += 8 - (offset % 8);
|
|
499
516
|
}
|
|
517
|
+
return ptr;
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const argc = this.argv.length;
|
|
521
|
+
|
|
522
|
+
const argvPtrs = [];
|
|
523
|
+
this.argv.forEach((arg) => {
|
|
524
|
+
argvPtrs.push(strPtr(arg));
|
|
525
|
+
});
|
|
526
|
+
argvPtrs.push(0);
|
|
527
|
+
|
|
528
|
+
const keys = Object.keys(this.env).sort();
|
|
529
|
+
keys.forEach((key) => {
|
|
530
|
+
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
|
|
531
|
+
});
|
|
532
|
+
argvPtrs.push(0);
|
|
533
|
+
|
|
534
|
+
const argv = offset;
|
|
535
|
+
argvPtrs.forEach((ptr) => {
|
|
536
|
+
this.mem.setUint32(offset, ptr, true);
|
|
537
|
+
this.mem.setUint32(offset + 4, 0, true);
|
|
538
|
+
offset += 8;
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
// The linker guarantees global data starts from at least wasmMinDataAddr.
|
|
542
|
+
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
|
|
543
|
+
const wasmMinDataAddr = 4096 + 8192;
|
|
544
|
+
if (offset >= wasmMinDataAddr) {
|
|
545
|
+
throw new Error("total length of command line and environment variables exceeds limit");
|
|
546
|
+
}
|
|
500
547
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this._inst.exports._initialize();
|
|
548
|
+
this._inst.exports.run(argc, argv);
|
|
549
|
+
if (this.exited) {
|
|
550
|
+
this._resolveExitPromise();
|
|
505
551
|
}
|
|
552
|
+
await this._exitPromise;
|
|
506
553
|
}
|
|
507
554
|
|
|
508
555
|
_resume() {
|
|
509
556
|
if (this.exited) {
|
|
510
557
|
throw new Error("Go program has already exited");
|
|
511
558
|
}
|
|
512
|
-
|
|
513
|
-
this._inst.exports.resume();
|
|
514
|
-
} catch (e) {
|
|
515
|
-
if (e !== wasmExit) throw e;
|
|
516
|
-
}
|
|
559
|
+
this._inst.exports.resume();
|
|
517
560
|
if (this.exited) {
|
|
518
561
|
this._resolveExitPromise();
|
|
519
562
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reteps/dockerfmt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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
|
},
|