@wasm-fmt/shfmt 0.0.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/LICENSE +21 -0
- package/README.md +65 -0
- package/package.json +55 -0
- package/shfmt.d.ts +56 -0
- package/shfmt.js +434 -0
- package/shfmt.wasm +0 -0
- package/shfmt_node.js +10 -0
- package/shfmt_vite.js +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wasm-fmt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[](https://github.com/wasm-fmt/shfmt/actions/workflows/test.yml)
|
|
2
|
+
|
|
3
|
+
# Install
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@wasm-fmt/shfmt)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @wasm-fmt/shfmt
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
[](https://jsr.io/@fmt/shfmt)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx jsr add @fmt/shfmt
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
# Usage
|
|
18
|
+
|
|
19
|
+
```JavaScript
|
|
20
|
+
import init, { format } from "@wasm-fmt/shfmt";
|
|
21
|
+
|
|
22
|
+
await init();
|
|
23
|
+
|
|
24
|
+
const source = `#!/bin/bash
|
|
25
|
+
echo "hello world"
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const formatted = format(source);
|
|
29
|
+
console.log(formatted);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
With options:
|
|
33
|
+
|
|
34
|
+
```JavaScript
|
|
35
|
+
const formatted = format(source, "script.sh", {
|
|
36
|
+
indent: 2,
|
|
37
|
+
binaryNextLine: true,
|
|
38
|
+
switchCaseIndent: false,
|
|
39
|
+
spaceRedirects: true,
|
|
40
|
+
keepPadding: false,
|
|
41
|
+
funcNextLine: false,
|
|
42
|
+
minify: false,
|
|
43
|
+
simplify: true
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
# Build from source
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 1. install Go https://go.dev/doc/install
|
|
51
|
+
|
|
52
|
+
# 2. install TinyGo https://tinygo.org/getting-started/install/
|
|
53
|
+
|
|
54
|
+
# 3. clone this repo
|
|
55
|
+
git clone https://github.com/wasm-fmt/shfmt.git
|
|
56
|
+
|
|
57
|
+
# 4. install dependencies inside the repo
|
|
58
|
+
npm
|
|
59
|
+
|
|
60
|
+
# 5. build
|
|
61
|
+
npm run build
|
|
62
|
+
|
|
63
|
+
# 6. test
|
|
64
|
+
npm run test:node
|
|
65
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wasm-fmt/shfmt",
|
|
3
|
+
"description": "A wasm based shell script formatter",
|
|
4
|
+
"author": "magic-akari <akari.ccino@gmail.com>",
|
|
5
|
+
"version": "0.0.0",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"wasm",
|
|
9
|
+
"shell",
|
|
10
|
+
"formatter",
|
|
11
|
+
"shfmt",
|
|
12
|
+
"bash"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/wasm-fmt/shfmt"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/wasm-fmt/shfmt",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/wasm-fmt/shfmt/issues"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "shfmt.js",
|
|
24
|
+
"module": "shfmt.js",
|
|
25
|
+
"types": "shfmt.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./shfmt.d.ts",
|
|
29
|
+
"node": "./shfmt_node.js",
|
|
30
|
+
"default": "./shfmt.js"
|
|
31
|
+
},
|
|
32
|
+
"./vite": {
|
|
33
|
+
"types": "./shfmt.d.ts",
|
|
34
|
+
"default": "./shfmt_vite.js"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json",
|
|
37
|
+
"./*": "./*"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "./scripts/build.sh",
|
|
41
|
+
"test:go": "go test -C src -v",
|
|
42
|
+
"test:node": "node --test test_node/shfmt.test.js",
|
|
43
|
+
"test:deno": "deno test test_deno --allow-read",
|
|
44
|
+
"test:bun": "bun test test_bun"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=16.17.0"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@ast-grep/napi": "0.40.3"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/shfmt.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type InitInput =
|
|
2
|
+
| RequestInfo
|
|
3
|
+
| URL
|
|
4
|
+
| Response
|
|
5
|
+
| BufferSource
|
|
6
|
+
| WebAssembly.Module;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the WASM module asynchronously.
|
|
10
|
+
* @param wasm_url - Optional URL/path to the WASM file, or any valid InitInput
|
|
11
|
+
*/
|
|
12
|
+
export default function initAsync(wasm_url?: InitInput): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Initializes the WASM module synchronously.
|
|
15
|
+
* @param module - The WASM module or buffer source
|
|
16
|
+
*/
|
|
17
|
+
export declare function initSync(
|
|
18
|
+
module: BufferSource | WebAssembly.Module,
|
|
19
|
+
): void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Formats a shell script source code.
|
|
23
|
+
* @param source - The shell script source code to format
|
|
24
|
+
* @param path - Optional file path for language detection (e.g., ".bash", ".sh")
|
|
25
|
+
* @param options - Formatting options
|
|
26
|
+
* @returns The formatted shell script
|
|
27
|
+
*/
|
|
28
|
+
export declare function format(
|
|
29
|
+
source: string,
|
|
30
|
+
path?: string,
|
|
31
|
+
options?: FormatOptions,
|
|
32
|
+
): string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Options for formatting shell scripts.
|
|
36
|
+
* These options correspond to shfmt's command-line flags.
|
|
37
|
+
* @see https://pkg.go.dev/mvdan.cc/sh/v3/syntax#PrinterOption
|
|
38
|
+
*/
|
|
39
|
+
export type FormatOptions = {
|
|
40
|
+
/** Indent sets the number of spaces used for indentation. If set to 0, tabs will be used instead. */
|
|
41
|
+
indent?: number;
|
|
42
|
+
/** BinaryNextLine will make binary operators appear on the next line when a binary command, such as a pipe, spans multiple lines. A backslash will be used. */
|
|
43
|
+
binaryNextLine?: boolean;
|
|
44
|
+
/** SwitchCaseIndent will make switch cases be indented. As such, switch case bodies will be two levels deeper than the switch itself. */
|
|
45
|
+
switchCaseIndent?: boolean;
|
|
46
|
+
/** SpaceRedirects will put a space after most redirection operators. The exceptions are '>&', '<&', '>(', and '<('. */
|
|
47
|
+
spaceRedirects?: boolean;
|
|
48
|
+
/** FunctionNextLine will place a function's opening braces on the next line. */
|
|
49
|
+
funcNextLine?: boolean;
|
|
50
|
+
/** Minify will print programs in a way to save the most bytes possible. For example, indentation and comments are skipped, and extra whitespace is avoided when possible. */
|
|
51
|
+
minify?: boolean;
|
|
52
|
+
/** SingleLine will attempt to print programs in one line. For example, lists of commands or nested blocks do not use newlines in this mode. Note that some newlines must still appear, such as those following comments or around here-documents. */
|
|
53
|
+
singleLine?: boolean;
|
|
54
|
+
/** Simplify will perform a series of simplifications on the AST, to prepare it for printing. */
|
|
55
|
+
simplify?: boolean;
|
|
56
|
+
};
|
package/shfmt.js
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/* @ts-self-types="./shfmt.d.ts" */
|
|
2
|
+
// Copyright 2018 The Go Authors. All rights reserved.
|
|
3
|
+
// Use of this source code is governed by a BSD-style
|
|
4
|
+
// license that can be found in the LICENSE file.
|
|
5
|
+
//
|
|
6
|
+
// This file has been modified for use by the TinyGo compiler.
|
|
7
|
+
|
|
8
|
+
// End of polyfills for common API.
|
|
9
|
+
|
|
10
|
+
const encoder = new TextEncoder("utf-8");
|
|
11
|
+
const decoder = new TextDecoder("utf-8");
|
|
12
|
+
let reinterpretBuf = new DataView(new ArrayBuffer(8));
|
|
13
|
+
var logLine = [];
|
|
14
|
+
const wasmExit = {}; // thrown to exit via proc_exit (not an error)
|
|
15
|
+
|
|
16
|
+
class Go {
|
|
17
|
+
constructor() {
|
|
18
|
+
this._callbackTimeouts = new Map();
|
|
19
|
+
this._nextCallbackTimeoutID = 1;
|
|
20
|
+
|
|
21
|
+
const mem = () => {
|
|
22
|
+
// The buffer may change when requesting more memory.
|
|
23
|
+
return new DataView(this._inst.exports.memory.buffer);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const unboxValue = (v_ref) => {
|
|
27
|
+
reinterpretBuf.setBigInt64(0, v_ref, true);
|
|
28
|
+
const f = reinterpretBuf.getFloat64(0, true);
|
|
29
|
+
if (f === 0) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
if (!isNaN(f)) {
|
|
33
|
+
return f;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const id = v_ref & 0xffffffffn;
|
|
37
|
+
return this._values[id];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const loadValue = (addr) => {
|
|
42
|
+
let v_ref = mem().getBigUint64(addr, true);
|
|
43
|
+
return unboxValue(v_ref);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const boxValue = (v) => {
|
|
47
|
+
const nanHead = 0x7FF80000n;
|
|
48
|
+
|
|
49
|
+
if (typeof v === "number") {
|
|
50
|
+
if (isNaN(v)) {
|
|
51
|
+
return nanHead << 32n;
|
|
52
|
+
}
|
|
53
|
+
if (v === 0) {
|
|
54
|
+
return (nanHead << 32n) | 1n;
|
|
55
|
+
}
|
|
56
|
+
reinterpretBuf.setFloat64(0, v, true);
|
|
57
|
+
return reinterpretBuf.getBigInt64(0, true);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
switch (v) {
|
|
61
|
+
case undefined:
|
|
62
|
+
return 0n;
|
|
63
|
+
case null:
|
|
64
|
+
return (nanHead << 32n) | 2n;
|
|
65
|
+
case true:
|
|
66
|
+
return (nanHead << 32n) | 3n;
|
|
67
|
+
case false:
|
|
68
|
+
return (nanHead << 32n) | 4n;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let id = this._ids.get(v);
|
|
72
|
+
if (id === undefined) {
|
|
73
|
+
id = this._idPool.pop();
|
|
74
|
+
if (id === undefined) {
|
|
75
|
+
id = BigInt(this._values.length);
|
|
76
|
+
}
|
|
77
|
+
this._values[id] = v;
|
|
78
|
+
this._goRefCounts[id] = 0;
|
|
79
|
+
this._ids.set(v, id);
|
|
80
|
+
}
|
|
81
|
+
this._goRefCounts[id]++;
|
|
82
|
+
let typeFlag = 1n;
|
|
83
|
+
switch (typeof v) {
|
|
84
|
+
case "string":
|
|
85
|
+
typeFlag = 2n;
|
|
86
|
+
break;
|
|
87
|
+
case "symbol":
|
|
88
|
+
typeFlag = 3n;
|
|
89
|
+
break;
|
|
90
|
+
case "function":
|
|
91
|
+
typeFlag = 4n;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
return id | ((nanHead | typeFlag) << 32n);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const storeValue = (addr, v) => {
|
|
98
|
+
let v_ref = boxValue(v);
|
|
99
|
+
mem().setBigUint64(addr, v_ref, true);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const loadSlice = (array, len, cap) => {
|
|
103
|
+
return new Uint8Array(this._inst.exports.memory.buffer, array, len);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const loadSliceOfValues = (array, len, cap) => {
|
|
107
|
+
const a = new Array(len);
|
|
108
|
+
for (let i = 0; i < len; i++) {
|
|
109
|
+
a[i] = loadValue(array + i * 8);
|
|
110
|
+
}
|
|
111
|
+
return a;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const loadString = (ptr, len) => {
|
|
115
|
+
return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const timeOrigin = Date.now() - performance.now();
|
|
119
|
+
this.importObject = {
|
|
120
|
+
wasi_snapshot_preview1: {
|
|
121
|
+
// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write
|
|
122
|
+
fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
|
|
123
|
+
let nwritten = 0;
|
|
124
|
+
if (fd == 1) {
|
|
125
|
+
for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
|
|
126
|
+
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
|
|
127
|
+
let ptr = mem().getUint32(iov_ptr + 0, true);
|
|
128
|
+
let len = mem().getUint32(iov_ptr + 4, true);
|
|
129
|
+
nwritten += len;
|
|
130
|
+
for (let i=0; i<len; i++) {
|
|
131
|
+
let c = mem().getUint8(ptr+i);
|
|
132
|
+
if (c == 13) { // CR
|
|
133
|
+
// ignore
|
|
134
|
+
} else if (c == 10) { // LF
|
|
135
|
+
// write line
|
|
136
|
+
let line = decoder.decode(new Uint8Array(logLine));
|
|
137
|
+
logLine = [];
|
|
138
|
+
console.log(line);
|
|
139
|
+
} else {
|
|
140
|
+
logLine.push(c);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
console.error('invalid file descriptor:', fd);
|
|
146
|
+
}
|
|
147
|
+
mem().setUint32(nwritten_ptr, nwritten, true);
|
|
148
|
+
return 0;
|
|
149
|
+
},
|
|
150
|
+
/* removed fd_close */ // dummy
|
|
151
|
+
/* removed fd_fdstat_get */ // dummy
|
|
152
|
+
/* removed fd_seek */ // dummy
|
|
153
|
+
proc_exit: (code) => {
|
|
154
|
+
this.exited = true;
|
|
155
|
+
this.exitCode = code;
|
|
156
|
+
this._resolveExitPromise();
|
|
157
|
+
throw wasmExit;
|
|
158
|
+
},
|
|
159
|
+
random_get: (bufPtr, bufLen) => {
|
|
160
|
+
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
|
|
161
|
+
return 0;
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
gojs: {
|
|
165
|
+
// func ticks() int64
|
|
166
|
+
"runtime.ticks": () => {
|
|
167
|
+
return BigInt((timeOrigin + performance.now()) * 1e6);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
// func sleepTicks(timeout int64)
|
|
171
|
+
/* removed runtime.sleepTicks */
|
|
172
|
+
|
|
173
|
+
// func finalizeRef(v ref)
|
|
174
|
+
"syscall/js.finalizeRef": (v_ref) => {
|
|
175
|
+
// Note: TinyGo does not support finalizers so this is only called
|
|
176
|
+
// for one specific case, by js.go:jsString. and can/might leak memory.
|
|
177
|
+
const id = v_ref & 0xffffffffn;
|
|
178
|
+
if (this._goRefCounts?.[id] !== undefined) {
|
|
179
|
+
this._goRefCounts[id]--;
|
|
180
|
+
if (this._goRefCounts[id] === 0) {
|
|
181
|
+
const v = this._values[id];
|
|
182
|
+
this._values[id] = null;
|
|
183
|
+
this._ids.delete(v);
|
|
184
|
+
this._idPool.push(id);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
console.error("syscall/js.finalizeRef: unknown id", id);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
// func stringVal(value string) ref
|
|
192
|
+
"syscall/js.stringVal": (value_ptr, value_len) => {
|
|
193
|
+
value_ptr >>>= 0;
|
|
194
|
+
const s = loadString(value_ptr, value_len);
|
|
195
|
+
return boxValue(s);
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
// func valueGet(v ref, p string) ref
|
|
199
|
+
"syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
|
|
200
|
+
let prop = loadString(p_ptr, p_len);
|
|
201
|
+
let v = unboxValue(v_ref);
|
|
202
|
+
let result = Reflect.get(v, prop);
|
|
203
|
+
return boxValue(result);
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
// func valueSet(v ref, p string, x ref)
|
|
207
|
+
"syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => {
|
|
208
|
+
const v = unboxValue(v_ref);
|
|
209
|
+
const p = loadString(p_ptr, p_len);
|
|
210
|
+
const x = unboxValue(x_ref);
|
|
211
|
+
Reflect.set(v, p, x);
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// func valueDelete(v ref, p string)
|
|
215
|
+
/* removed syscall/js.valueDelete */
|
|
216
|
+
|
|
217
|
+
// func valueIndex(v ref, i int) ref
|
|
218
|
+
"syscall/js.valueIndex": (v_ref, i) => {
|
|
219
|
+
return boxValue(Reflect.get(unboxValue(v_ref), i));
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
// valueSetIndex(v ref, i int, x ref)
|
|
223
|
+
"syscall/js.valueSetIndex": (v_ref, i, x_ref) => {
|
|
224
|
+
Reflect.set(unboxValue(v_ref), i, unboxValue(x_ref));
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
|
228
|
+
"syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => {
|
|
229
|
+
const v = unboxValue(v_ref);
|
|
230
|
+
const name = loadString(m_ptr, m_len);
|
|
231
|
+
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
|
232
|
+
try {
|
|
233
|
+
const m = Reflect.get(v, name);
|
|
234
|
+
storeValue(ret_addr, Reflect.apply(m, v, args));
|
|
235
|
+
mem().setUint8(ret_addr + 8, 1);
|
|
236
|
+
} catch (err) {
|
|
237
|
+
storeValue(ret_addr, err);
|
|
238
|
+
mem().setUint8(ret_addr + 8, 0);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
// func valueInvoke(v ref, args []ref) (ref, bool)
|
|
243
|
+
/* removed syscall/js.valueInvoke */
|
|
244
|
+
|
|
245
|
+
// func valueNew(v ref, args []ref) (ref, bool)
|
|
246
|
+
"syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
|
|
247
|
+
const v = unboxValue(v_ref);
|
|
248
|
+
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
|
249
|
+
try {
|
|
250
|
+
storeValue(ret_addr, Reflect.construct(v, args));
|
|
251
|
+
mem().setUint8(ret_addr + 8, 1);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
storeValue(ret_addr, err);
|
|
254
|
+
mem().setUint8(ret_addr+ 8, 0);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
// func valueLength(v ref) int
|
|
259
|
+
"syscall/js.valueLength": (v_ref) => {
|
|
260
|
+
return unboxValue(v_ref).length;
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
// valuePrepareString(v ref) (ref, int)
|
|
264
|
+
"syscall/js.valuePrepareString": (ret_addr, v_ref) => {
|
|
265
|
+
const s = String(unboxValue(v_ref));
|
|
266
|
+
const str = encoder.encode(s);
|
|
267
|
+
storeValue(ret_addr, str);
|
|
268
|
+
mem().setInt32(ret_addr + 8, str.length, true);
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
// valueLoadString(v ref, b []byte)
|
|
272
|
+
"syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
|
|
273
|
+
const str = unboxValue(v_ref);
|
|
274
|
+
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
// func valueInstanceOf(v ref, t ref) bool
|
|
278
|
+
/* removed syscall/js.valueInstanceOf */
|
|
279
|
+
|
|
280
|
+
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
|
281
|
+
/* removed syscall/js.copyBytesToGo */
|
|
282
|
+
|
|
283
|
+
// copyBytesToJS(dst ref, src []byte) (int, bool)
|
|
284
|
+
// Originally copied from upstream Go project, then modified:
|
|
285
|
+
// https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
|
|
286
|
+
/* removed syscall/js.copyBytesToJS */
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Go 1.20 uses 'env'. Go 1.21 uses 'gojs'.
|
|
291
|
+
// For compatibility, we use both as long as Go 1.20 is supported.
|
|
292
|
+
this.importObject.env = this.importObject.gojs;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async run(instance) {
|
|
296
|
+
this._inst = instance;
|
|
297
|
+
this._values = [ // JS values that Go currently has references to, indexed by reference id
|
|
298
|
+
NaN,
|
|
299
|
+
0,
|
|
300
|
+
null,
|
|
301
|
+
true,
|
|
302
|
+
false,
|
|
303
|
+
/* fake global */ { set format(fn) { _format = fn }, Array, Object},
|
|
304
|
+
this,
|
|
305
|
+
];
|
|
306
|
+
this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
|
|
307
|
+
this._ids = new Map(); // mapping from JS values to reference ids
|
|
308
|
+
this._idPool = []; // unused ids that have been garbage collected
|
|
309
|
+
this.exited = false; // whether the Go program has exited
|
|
310
|
+
this.exitCode = 0;
|
|
311
|
+
|
|
312
|
+
if (this._inst.exports._start) {
|
|
313
|
+
let exitPromise = new Promise((resolve, reject) => {
|
|
314
|
+
this._resolveExitPromise = resolve;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Run program, but catch the wasmExit exception that's thrown
|
|
318
|
+
// to return back here.
|
|
319
|
+
try {
|
|
320
|
+
this._inst.exports._start();
|
|
321
|
+
} catch (e) {
|
|
322
|
+
if (e !== wasmExit) throw e;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
await exitPromise;
|
|
326
|
+
return this.exitCode;
|
|
327
|
+
} else {
|
|
328
|
+
this._inst.exports._initialize();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
_resume() {
|
|
333
|
+
if (this.exited) {
|
|
334
|
+
throw new Error("Go program has already exited");
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
this._inst.exports.resume();
|
|
338
|
+
} catch (e) {
|
|
339
|
+
if (e !== wasmExit) throw e;
|
|
340
|
+
}
|
|
341
|
+
if (this.exited) {
|
|
342
|
+
this._resolveExitPromise();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
_makeFuncWrapper(id) {
|
|
347
|
+
const go = this;
|
|
348
|
+
return function () {
|
|
349
|
+
const event = { id: id, this: this, args: arguments };
|
|
350
|
+
go._pendingEvent = event;
|
|
351
|
+
go._resume();
|
|
352
|
+
return event.result;
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* ================== End of wasm_exec.js ==================
|
|
358
|
+
*/
|
|
359
|
+
let wasm, wasmModule;
|
|
360
|
+
async function __load(module, imports) {
|
|
361
|
+
if (typeof Response === "function" && module instanceof Response) {
|
|
362
|
+
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
363
|
+
try {
|
|
364
|
+
return await WebAssembly.instantiateStreaming(module, imports);
|
|
365
|
+
} catch (e) {
|
|
366
|
+
if (module.headers.get("Content-Type") != "application/wasm") {
|
|
367
|
+
console.warn(
|
|
368
|
+
"`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",
|
|
369
|
+
e,
|
|
370
|
+
);
|
|
371
|
+
} else {
|
|
372
|
+
throw e;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const bytes = await module.arrayBuffer();
|
|
377
|
+
return await WebAssembly.instantiate(bytes, imports);
|
|
378
|
+
} else {
|
|
379
|
+
const instance = await WebAssembly.instantiate(module, imports);
|
|
380
|
+
if (instance instanceof WebAssembly.Instance)
|
|
381
|
+
return { instance, module };
|
|
382
|
+
else return instance;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function __finalize_init(instance, module) {
|
|
386
|
+
(wasm = instance.exports), (wasmModule = module);
|
|
387
|
+
return wasm;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function initSync(module) {
|
|
391
|
+
if (wasm !== void 0) return wasm;
|
|
392
|
+
|
|
393
|
+
const go = new Go();
|
|
394
|
+
const imports = go.importObject;
|
|
395
|
+
|
|
396
|
+
if (!(module instanceof WebAssembly.Module))
|
|
397
|
+
module = new WebAssembly.Module(module);
|
|
398
|
+
|
|
399
|
+
const instance = new WebAssembly.Instance(module, imports);
|
|
400
|
+
|
|
401
|
+
go.run(instance);
|
|
402
|
+
return __finalize_init(instance, module);
|
|
403
|
+
}
|
|
404
|
+
export default async function initAsync(input) {
|
|
405
|
+
if (wasm !== void 0) return wasm;
|
|
406
|
+
|
|
407
|
+
if (input === void 0) input = new URL("shfmt.wasm", import.meta.url);
|
|
408
|
+
|
|
409
|
+
const go = new Go();
|
|
410
|
+
const imports = go.importObject;
|
|
411
|
+
|
|
412
|
+
if (
|
|
413
|
+
typeof input === "string" ||
|
|
414
|
+
(typeof Request === "function" && input instanceof Request) ||
|
|
415
|
+
(typeof URL === "function" && input instanceof URL)
|
|
416
|
+
) {
|
|
417
|
+
input = fetch(input);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const { instance, module } = await __load(await input, imports);
|
|
421
|
+
|
|
422
|
+
go.run(instance);
|
|
423
|
+
return __finalize_init(instance, module);
|
|
424
|
+
}
|
|
425
|
+
var _format = function () {
|
|
426
|
+
throw new Error("wasm not initialized.");
|
|
427
|
+
};
|
|
428
|
+
export function format(input, path, options) {
|
|
429
|
+
const [result, err] = _format(input, path, options);
|
|
430
|
+
if (err) {
|
|
431
|
+
throw new Error(result);
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
}
|
package/shfmt.wasm
ADDED
|
Binary file
|
package/shfmt_node.js
ADDED