@ttsc/wasm 0.30.4 → 0.31.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 +3 -3
- package/build/build-wasm.cjs +5 -4
- package/build/go-build-cache.cjs +347 -0
- package/build/pack-prepare.cjs +3 -2
- package/cmd/ttsc-wasm/main.go +22 -10
- package/cmd/ttsc-wasm/main_wasm.go +2 -2
- package/dist/.ttsc-wasm-build.json +138 -58
- package/dist/go.mod +7 -5
- package/dist/ttsc.wasm +2 -2
- package/go.mod +7 -5
- package/go.sum +10 -10
- package/host/api.go +34 -41
- package/host/doc.go +7 -6
- package/host/expose.go +42 -0
- package/host/fountain.go +192 -22
- package/host/host.go +10 -25
- package/host/host_native.go +3 -5
- package/host/invoke.go +133 -0
- package/host/plugin.go +37 -72
- package/host/result.go +108 -0
- package/lib/src/BootTtscWorkerTerminationError.d.ts +20 -0
- package/lib/src/BootTtscWorkerTerminationError.js +20 -0
- package/lib/src/BootTtscWorkerTerminationError.js.map +1 -1
- package/lib/src/MemFSError.d.ts +21 -2
- package/lib/src/MemFSError.js +25 -2
- package/lib/src/MemFSError.js.map +1 -1
- package/lib/src/bootTtsc.d.ts +49 -2
- package/lib/src/bootTtsc.js +89 -23
- package/lib/src/bootTtsc.js.map +1 -1
- package/lib/src/createMemFS.d.ts +45 -0
- package/lib/src/createMemFS.js +269 -66
- package/lib/src/createMemFS.js.map +1 -1
- package/lib/src/parseResult.d.ts +19 -0
- package/lib/src/parseResult.js +19 -0
- package/lib/src/parseResult.js.map +1 -1
- package/lib/src/structures/IBootResult.d.ts +20 -2
- package/lib/src/structures/IBootTtscOptions.d.ts +28 -6
- package/lib/src/structures/IFileStats.d.ts +51 -0
- package/lib/src/structures/IMemFSHost.d.ts +89 -0
- package/lib/src/structures/ITtscApi.d.ts +154 -9
- package/lib/src/structures/ITtscBuildOpts.d.ts +18 -1
- package/lib/src/structures/ITtscCompileResult.d.ts +20 -2
- package/lib/src/structures/ITtscDiagnostic.d.ts +25 -1
- package/lib/src/structures/ITtscFileQuery.d.ts +19 -1
- package/lib/src/structures/ITtscFountainDiagnosticsResult.d.ts +20 -1
- package/lib/src/structures/ITtscNodeAtPositionResult.d.ts +18 -1
- package/lib/src/structures/ITtscNodeInfo.d.ts +23 -2
- package/lib/src/structures/ITtscPluginOpts.d.ts +24 -3
- package/lib/src/structures/ITtscPositionQuery.d.ts +21 -4
- package/lib/src/structures/ITtscReleaseSnapshotResult.d.ts +22 -2
- package/lib/src/structures/ITtscResult.d.ts +31 -5
- package/lib/src/structures/ITtscSnapshotHandle.d.ts +19 -1
- package/lib/src/structures/ITtscSnapshotResult.d.ts +22 -1
- package/lib/src/structures/ITtscSnapshotsResult.d.ts +19 -1
- package/lib/src/structures/ITtscSourceFileTextResult.d.ts +19 -1
- package/lib/src/structures/ITtscSourceFilesResult.d.ts +23 -2
- package/lib/src/structures/ITtscSymbolAtPositionResult.d.ts +18 -1
- package/lib/src/structures/ITtscSymbolDeclaration.d.ts +25 -2
- package/lib/src/structures/ITtscSymbolInfo.d.ts +19 -1
- package/lib/src/structures/ITtscTransformResult.d.ts +21 -3
- package/lib/src/structures/ITtscTypeAtPositionResult.d.ts +18 -1
- package/lib/src/structures/ITtscTypeInfo.d.ts +19 -1
- package/lib/src/structures/ITtscVersion.d.ts +28 -1
- package/lib/src/structures/IWasmExecFS.d.ts +310 -0
- package/package.json +4 -2
- package/shim-vendor/shim/ast/enums_gen.go +4 -2
- package/shim-vendor/shim/ast/go.mod +5 -5
- package/shim-vendor/shim/ast/go.sum +10 -10
- package/shim-vendor/shim/ast/lint.go +1088 -20
- package/shim-vendor/shim/ast/parent.go +38 -22
- package/shim-vendor/shim/ast/safe_text.go +48 -26
- package/shim-vendor/shim/ast/shim.go +1307 -20
- package/shim-vendor/shim/ast/test/next_jsdoc_comment_location_reaches_variable_statement_test.go +51 -0
- package/shim-vendor/shim/ast/test/node_text_joins_multi_hop_qualified_name_test.go +24 -9
- package/shim-vendor/shim/ast/test/node_text_joins_one_hop_qualified_name_test.go +9 -5
- package/shim-vendor/shim/ast/test/node_text_returns_empty_for_nil_test.go +13 -5
- package/shim-vendor/shim/ast/test/node_text_returns_identifier_text_test.go +12 -5
- package/shim-vendor/shim/ast/test/node_text_skips_empty_qualified_left_test.go +35 -5
- package/shim-vendor/shim/astnav/go.mod +5 -5
- package/shim-vendor/shim/astnav/go.sum +12 -10
- package/shim-vendor/shim/astnav/shim.go +9 -0
- package/shim-vendor/shim/bundled/go.mod +7 -1
- package/shim-vendor/shim/bundled/go.sum +12 -0
- package/shim-vendor/shim/checker/enums_gen.go +4 -2
- package/shim-vendor/shim/checker/extra-shim.json +6 -0
- package/shim-vendor/shim/checker/go.mod +5 -5
- package/shim-vendor/shim/checker/go.sum +10 -10
- package/shim-vendor/shim/checker/node_builder_fields_gen.go +19 -0
- package/shim-vendor/shim/checker/shim.go +827 -114
- package/shim-vendor/shim/compiler/emit_sourcemap.go +68 -29
- package/shim-vendor/shim/compiler/emitassemble.go +36 -2
- package/shim-vendor/shim/compiler/emithook.go +13 -3
- package/shim-vendor/shim/compiler/go.mod +5 -5
- package/shim-vendor/shim/compiler/go.sum +10 -10
- package/shim-vendor/shim/compiler/incremental.go +56 -20
- package/shim-vendor/shim/compiler/resolution.go +92 -9
- package/shim-vendor/shim/core/enums_gen.go +2 -2
- package/shim-vendor/shim/core/go.mod +6 -1
- package/shim-vendor/shim/core/go.sum +10 -0
- package/shim-vendor/shim/core/modulekind.go +9 -0
- package/shim-vendor/shim/core/shim.go +157 -6
- package/shim-vendor/shim/diagnosticwriter/go.mod +10 -1
- package/shim-vendor/shim/diagnosticwriter/go.sum +20 -0
- package/shim-vendor/shim/diagnosticwriter/lint.go +188 -10
- package/shim-vendor/shim/diagnosticwriter/shim.go +14 -0
- package/shim-vendor/shim/lsp/go.mod +5 -5
- package/shim-vendor/shim/lsp/go.sum +10 -10
- package/shim-vendor/shim/lsp/shim.go +78 -2
- package/shim-vendor/shim/parser/go.mod +10 -1
- package/shim-vendor/shim/parser/go.sum +22 -0
- package/shim-vendor/shim/printer/emithost.go +46 -1
- package/shim-vendor/shim/printer/go.mod +5 -5
- package/shim-vendor/shim/printer/go.sum +10 -10
- package/shim-vendor/shim/printer/shim.go +114 -7
- package/shim-vendor/shim/printer/sourcemap.go +55 -0
- package/shim-vendor/shim/scanner/go.mod +10 -1
- package/shim-vendor/shim/scanner/go.sum +20 -0
- package/shim-vendor/shim/scanner/regexp.go +19 -4
- package/shim-vendor/shim/scanner/shim.go +3 -0
- package/shim-vendor/shim/stringutil/extra-shim.json +1 -0
- package/shim-vendor/shim/stringutil/go.mod +5 -0
- package/shim-vendor/shim/stringutil/go.sum +2 -0
- package/shim-vendor/shim/stringutil/shim.go +119 -0
- package/shim-vendor/shim/transformers/go.mod +5 -5
- package/shim-vendor/shim/transformers/go.sum +12 -10
- package/shim-vendor/shim/transformers/shim.go +12 -3
- package/shim-vendor/shim/tsoptions/command_line_raw.go +44 -0
- package/shim-vendor/shim/tsoptions/go.mod +10 -1
- package/shim-vendor/shim/tsoptions/go.sum +22 -0
- package/shim-vendor/shim/tsoptions/shim.go +1 -1
- package/shim-vendor/shim/tspath/go.mod +1 -1
- package/shim-vendor/shim/tspath/go.sum +6 -0
- package/shim-vendor/shim/vfs/cachedvfs/go.mod +3 -1
- package/shim-vendor/shim/vfs/cachedvfs/go.sum +12 -0
- package/shim-vendor/shim/vfs/decode.go +26 -0
- package/shim-vendor/shim/vfs/decode_test.go +42 -0
- package/shim-vendor/shim/vfs/go.mod +7 -1
- package/shim-vendor/shim/vfs/go.sum +12 -0
- package/shim-vendor/shim/vfs/osvfs/go.mod +7 -1
- package/shim-vendor/shim/vfs/osvfs/go.sum +12 -0
- package/src/BootTtscWorkerTerminationError.ts +22 -0
- package/src/MemFSError.ts +29 -2
- package/src/bootTtsc.ts +104 -28
- package/src/createMemFS.ts +291 -64
- package/src/parseResult.ts +19 -0
- package/src/structures/IBootResult.ts +21 -2
- package/src/structures/IBootTtscOptions.ts +32 -6
- package/src/structures/IFileStats.ts +65 -0
- package/src/structures/IMemFSHost.ts +97 -0
- package/src/structures/ITtscApi.ts +154 -9
- package/src/structures/ITtscBuildOpts.ts +19 -1
- package/src/structures/ITtscCompileResult.ts +21 -2
- package/src/structures/ITtscDiagnostic.ts +32 -1
- package/src/structures/ITtscFileQuery.ts +19 -1
- package/src/structures/ITtscFountainDiagnosticsResult.ts +20 -1
- package/src/structures/ITtscNodeAtPositionResult.ts +18 -1
- package/src/structures/ITtscNodeInfo.ts +27 -2
- package/src/structures/ITtscPluginOpts.ts +28 -3
- package/src/structures/ITtscPositionQuery.ts +21 -4
- package/src/structures/ITtscReleaseSnapshotResult.ts +22 -2
- package/src/structures/ITtscResult.ts +34 -5
- package/src/structures/ITtscSnapshotHandle.ts +19 -1
- package/src/structures/ITtscSnapshotResult.ts +22 -1
- package/src/structures/ITtscSnapshotsResult.ts +19 -1
- package/src/structures/ITtscSourceFileTextResult.ts +19 -1
- package/src/structures/ITtscSourceFilesResult.ts +23 -2
- package/src/structures/ITtscSymbolAtPositionResult.ts +18 -1
- package/src/structures/ITtscSymbolDeclaration.ts +27 -2
- package/src/structures/ITtscSymbolInfo.ts +23 -1
- package/src/structures/ITtscTransformResult.ts +22 -3
- package/src/structures/ITtscTypeAtPositionResult.ts +18 -1
- package/src/structures/ITtscTypeInfo.ts +20 -1
- package/src/structures/ITtscVersion.ts +33 -1
- package/src/structures/IWasmExecFS.ts +336 -0
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ There is no boot deadline. How long a fetch and instantiation take belongs to th
|
|
|
118
118
|
|
|
119
119
|
Once `go.run` starts, JavaScript cannot terminate the Go wasm runtime. A boot failure from that point throws `BootTtscWorkerTerminationError` with code `TTSC_WASM_WORKER_TERMINATION_REQUIRED`, and later boots for the same `apiName` reject without installing another readiness bridge. Terminate and replace that Worker before retrying.
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
Use one Go runtime per Worker. A different API name does not isolate the shared `Go`, `fs` and `process` globals, so another binary needs another Worker. Calls joining the same API/binary boot reuse its actual filesystem; a different explicit host or `wasmExecUrl` is rejected rather than silently ignored. The default runtime-script URL is the binary's sibling `wasm_exec.js`, without its query or fragment.
|
|
122
122
|
|
|
123
123
|
## Fountain API (snapshot, AST, type checker)
|
|
124
124
|
|
|
@@ -171,7 +171,7 @@ Verbs and payload types:
|
|
|
171
171
|
|
|
172
172
|
`getNodeAtPosition` returns the syntax token touching `position`, including punctuation. `getTypeAtPosition` resolves that token to its enclosing semantic AST node, while `getSymbolAtPosition` queries the token directly. Both return `null` when there is no semantic answer. Whitespace and comments return `null` for all three verbs.
|
|
173
173
|
|
|
174
|
-
`position` is a **byte offset** into the source text and must satisfy `0 <= position < UTF-8 byte length of sourceText`.
|
|
174
|
+
`position` is a finite integer **byte offset** into the source text and must satisfy `0 <= position < UTF-8 byte length of sourceText`. This is the coordinate TypeScript-Go uses internally. Fractional and non-finite values are rejected before integer conversion. JS callers that have a UTF-16 `(line, character)` pair (e.g. From Monaco) must convert it before calling.
|
|
175
175
|
|
|
176
176
|
**Lifecycle:** JS owns the handle. The wasm keeps the program (parsed AST, checker pool lease, every source file) alive until you call `releaseSnapshot`. Leaking handles leaks memory in the wasm linear heap.
|
|
177
177
|
|
|
@@ -195,7 +195,7 @@ Use `invocation.Go` for asynchronous work that belongs to the result. Register i
|
|
|
195
195
|
The published `@ttsc/wasm` tarball ships:
|
|
196
196
|
|
|
197
197
|
- A rewritten root `go.mod` whose `replace` directives point at `./shim-vendor/shim/*` (vendored at pack time from `packages/ttsc/shim/`).
|
|
198
|
-
- The full `host
|
|
198
|
+
- The full `host/` and `cmd/` Go source and the `build/` scripts so consumers can `go build -tags '...'` their own wasm against your host helper.
|
|
199
199
|
- `dist/ttsc.wasm` + `dist/wasm_exec.js`: the no-plugin sanity binary and the Go runtime loader.
|
|
200
200
|
|
|
201
201
|
The tarball intentionally drops the `replace github.com/samchon/ttsc/packages/ttsc => ../ttsc` directive that the in-repo `go.mod` carries: consumers of the published module who want to rebuild the wasm must supply their own `replace` (or vendor `packages/ttsc` themselves). The published `dist/ttsc.wasm` is plug-and-play for runtime use; the Go module is for **plugin authors extending the host**, not for vanilla consumers.
|
package/build/build-wasm.cjs
CHANGED
|
@@ -10,7 +10,7 @@ const cp = require("child_process");
|
|
|
10
10
|
const fs = require("fs");
|
|
11
11
|
const path = require("path");
|
|
12
12
|
|
|
13
|
-
const { createGoBuildCache } = require("
|
|
13
|
+
const { createGoBuildCache } = require("./go-build-cache.cjs");
|
|
14
14
|
|
|
15
15
|
const packageRoot = path.resolve(__dirname, "..");
|
|
16
16
|
const repoRoot = path.resolve(packageRoot, "..", "..");
|
|
@@ -129,9 +129,10 @@ function buildWasm() {
|
|
|
129
129
|
// Mirror packages/ttsc/shim/* into packages/wasm/shim-vendor/shim/* so the
|
|
130
130
|
// published @ttsc/wasm tarball is self-contained: consumers extending the
|
|
131
131
|
// wasm binary against the @ttsc/wasm Go module no longer need a sibling
|
|
132
|
-
// `packages/ttsc/shim/` working tree. The vendored copy is
|
|
133
|
-
// `
|
|
134
|
-
//
|
|
132
|
+
// `packages/ttsc/shim/` working tree. The vendored copy is a build artifact:
|
|
133
|
+
// `shim-vendor/` is git-ignored and regenerated by every uncached build, and
|
|
134
|
+
// its files are snapshotted with the cache artifacts, so an interrupted or
|
|
135
|
+
// edited copy is rebuilt. The working-tree `go.mod` still points at `../ttsc/shim/*` for
|
|
135
136
|
// local dev; the published `go.mod` is rewritten into `dist/go.mod` and
|
|
136
137
|
// swapped in via prepack/postpack.
|
|
137
138
|
function vendorShim() {
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
const cp = require("node:child_process");
|
|
2
|
+
const crypto = require("node:crypto");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const os = require("node:os");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
const SOURCE_FIELDS = [
|
|
8
|
+
"GoFiles",
|
|
9
|
+
"CgoFiles",
|
|
10
|
+
"CFiles",
|
|
11
|
+
"CXXFiles",
|
|
12
|
+
"MFiles",
|
|
13
|
+
"HFiles",
|
|
14
|
+
"FFiles",
|
|
15
|
+
"SFiles",
|
|
16
|
+
"SysoFiles",
|
|
17
|
+
"EmbedFiles",
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function createGoBuildCache({
|
|
21
|
+
artifactPaths,
|
|
22
|
+
buildArguments,
|
|
23
|
+
cachePath,
|
|
24
|
+
cwd,
|
|
25
|
+
dependencyPackages,
|
|
26
|
+
environment,
|
|
27
|
+
extraFiles = [],
|
|
28
|
+
force = false,
|
|
29
|
+
inputDirectories = [],
|
|
30
|
+
execFileSync = cp.execFileSync,
|
|
31
|
+
}) {
|
|
32
|
+
const identity = createInputIdentity({
|
|
33
|
+
buildArguments,
|
|
34
|
+
cwd,
|
|
35
|
+
dependencyPackages,
|
|
36
|
+
environment,
|
|
37
|
+
extraFiles,
|
|
38
|
+
inputDirectories,
|
|
39
|
+
execFileSync,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
identity,
|
|
44
|
+
isCurrent() {
|
|
45
|
+
if (force) return false;
|
|
46
|
+
const record = readRecord(cachePath);
|
|
47
|
+
if (record?.identity !== identity.hash) return false;
|
|
48
|
+
try {
|
|
49
|
+
return (
|
|
50
|
+
stableJson(record.artifacts) ===
|
|
51
|
+
stableJson(snapshotPaths(artifactPaths))
|
|
52
|
+
);
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
write() {
|
|
58
|
+
const record = {
|
|
59
|
+
schema: 1,
|
|
60
|
+
identity: identity.hash,
|
|
61
|
+
artifacts: snapshotPaths(artifactPaths),
|
|
62
|
+
};
|
|
63
|
+
writeAtomically(cachePath, `${JSON.stringify(record, null, 2)}\n`);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function createInputIdentity({
|
|
69
|
+
buildArguments,
|
|
70
|
+
cwd,
|
|
71
|
+
dependencyPackages,
|
|
72
|
+
environment,
|
|
73
|
+
extraFiles,
|
|
74
|
+
inputDirectories,
|
|
75
|
+
execFileSync,
|
|
76
|
+
}) {
|
|
77
|
+
// Dependency JSON grows with the entire selected package graph. Give stdout
|
|
78
|
+
// a file descriptor instead of execFileSync's bounded capture pipe; parse
|
|
79
|
+
// every byte only after Go succeeds, and reclaim the descriptor and file on
|
|
80
|
+
// both the success and failure paths. The temporary input is outside cwd so
|
|
81
|
+
// directory-shaped cache inputs cannot observe it.
|
|
82
|
+
const runGo = (args) => {
|
|
83
|
+
const directory = fs.mkdtempSync(path.join(os.tmpdir(), "ttsc-go-list-"));
|
|
84
|
+
const output = path.join(directory, "stdout.json");
|
|
85
|
+
let descriptor;
|
|
86
|
+
try {
|
|
87
|
+
descriptor = fs.openSync(output, "wx");
|
|
88
|
+
execFileSync("go", args, {
|
|
89
|
+
cwd,
|
|
90
|
+
env: { ...process.env, ...environment },
|
|
91
|
+
stdio: ["ignore", descriptor, "inherit"],
|
|
92
|
+
windowsHide: true,
|
|
93
|
+
});
|
|
94
|
+
return fs.readFileSync(output, "utf8");
|
|
95
|
+
} finally {
|
|
96
|
+
try {
|
|
97
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
98
|
+
} finally {
|
|
99
|
+
fs.rmSync(directory, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const packages = parseJsonStream(
|
|
104
|
+
runGo(["list", "-deps", "-json", ...dependencyPackages]),
|
|
105
|
+
);
|
|
106
|
+
const modules = parseJsonStream(runGo(["list", "-m", "-json", "all"]));
|
|
107
|
+
const toolchain = JSON.parse(
|
|
108
|
+
runGo([
|
|
109
|
+
"env",
|
|
110
|
+
"-json",
|
|
111
|
+
"GOVERSION",
|
|
112
|
+
"GOROOT",
|
|
113
|
+
"GOOS",
|
|
114
|
+
"GOARCH",
|
|
115
|
+
"GOFLAGS",
|
|
116
|
+
"GOEXPERIMENT",
|
|
117
|
+
"GOTOOLCHAIN",
|
|
118
|
+
"GOTOOLDIR",
|
|
119
|
+
"GOWASM",
|
|
120
|
+
"CGO_ENABLED",
|
|
121
|
+
"GOWORK",
|
|
122
|
+
]),
|
|
123
|
+
);
|
|
124
|
+
addToolchainBinaryIdentity(toolchain);
|
|
125
|
+
const files = new Set([path.resolve(__filename)]);
|
|
126
|
+
|
|
127
|
+
for (const pkg of packages) {
|
|
128
|
+
if (!pkg.Dir) continue;
|
|
129
|
+
for (const field of SOURCE_FIELDS) {
|
|
130
|
+
for (const file of pkg[field] ?? []) {
|
|
131
|
+
files.add(path.join(pkg.Dir, file));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (pkg.Module?.GoMod) addIfFile(files, pkg.Module.GoMod);
|
|
135
|
+
}
|
|
136
|
+
for (const mod of modules) {
|
|
137
|
+
if (!mod.GoMod) continue;
|
|
138
|
+
addIfFile(files, mod.GoMod);
|
|
139
|
+
addIfFile(files, path.join(path.dirname(mod.GoMod), "go.sum"));
|
|
140
|
+
}
|
|
141
|
+
if (toolchain.GOWORK && toolchain.GOWORK !== "off") {
|
|
142
|
+
addIfFile(files, toolchain.GOWORK);
|
|
143
|
+
addIfFile(files, path.join(path.dirname(toolchain.GOWORK), "go.work.sum"));
|
|
144
|
+
}
|
|
145
|
+
for (const file of extraFiles) files.add(path.resolve(file));
|
|
146
|
+
for (const directory of inputDirectories) addDirectory(files, directory);
|
|
147
|
+
|
|
148
|
+
const inputs = [...files]
|
|
149
|
+
.sort((a, b) =>
|
|
150
|
+
normalizedPath(a, cwd).localeCompare(normalizedPath(b, cwd)),
|
|
151
|
+
)
|
|
152
|
+
.map((file) => ({
|
|
153
|
+
path: normalizedPath(file, cwd),
|
|
154
|
+
sha256: fileDigest(file),
|
|
155
|
+
}));
|
|
156
|
+
const payload = {
|
|
157
|
+
schema: 1,
|
|
158
|
+
buildArguments,
|
|
159
|
+
environment,
|
|
160
|
+
toolchain,
|
|
161
|
+
inputs,
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
hash: digest(stableJson(payload)),
|
|
165
|
+
payload,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function addToolchainBinaryIdentity(toolchain) {
|
|
170
|
+
const goroot = toolchain.GOROOT;
|
|
171
|
+
const goToolDir = toolchain.GOTOOLDIR;
|
|
172
|
+
delete toolchain.GOROOT;
|
|
173
|
+
delete toolchain.GOTOOLDIR;
|
|
174
|
+
if (!goroot) return;
|
|
175
|
+
const executable = path.join(
|
|
176
|
+
goroot,
|
|
177
|
+
"bin",
|
|
178
|
+
process.platform === "win32" ? "go.exe" : "go",
|
|
179
|
+
);
|
|
180
|
+
try {
|
|
181
|
+
toolchain.goBinarySha256 = fileDigest(executable);
|
|
182
|
+
} catch {
|
|
183
|
+
// `go env` supplied a toolchain root, but the compiler binary vanished
|
|
184
|
+
// before its identity could be read. Keep the miss deterministic instead
|
|
185
|
+
// of reusing an artifact built by an unknown compiler.
|
|
186
|
+
toolchain.goBinarySha256 = null;
|
|
187
|
+
}
|
|
188
|
+
if (!goToolDir) return;
|
|
189
|
+
try {
|
|
190
|
+
toolchain.goToolDirectorySha256 = directoryDigest(goToolDir);
|
|
191
|
+
} catch {
|
|
192
|
+
// The compiler driver may still be present when an incomplete toolchain
|
|
193
|
+
// loses its compile/link programs. Treat that transition as a cache miss.
|
|
194
|
+
toolchain.goToolDirectorySha256 = null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function directoryDigest(directory) {
|
|
199
|
+
const files = new Set();
|
|
200
|
+
addDirectory(files, directory);
|
|
201
|
+
return digest(
|
|
202
|
+
stableJson(
|
|
203
|
+
[...files]
|
|
204
|
+
.sort()
|
|
205
|
+
.map((file) => ({
|
|
206
|
+
path: path.relative(directory, file).replaceAll(path.sep, "/"),
|
|
207
|
+
sha256: fileDigest(file),
|
|
208
|
+
})),
|
|
209
|
+
),
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function parseJsonStream(source) {
|
|
214
|
+
const records = [];
|
|
215
|
+
let start = 0;
|
|
216
|
+
while (start < source.length) {
|
|
217
|
+
while (/\s/.test(source[start] ?? "")) start += 1;
|
|
218
|
+
if (start >= source.length) break;
|
|
219
|
+
if (source[start] !== "{") {
|
|
220
|
+
throw new Error("go command returned a non-JSON record");
|
|
221
|
+
}
|
|
222
|
+
let depth = 0;
|
|
223
|
+
let quoted = false;
|
|
224
|
+
let escaped = false;
|
|
225
|
+
let end = start;
|
|
226
|
+
for (; end < source.length; end += 1) {
|
|
227
|
+
const character = source[end];
|
|
228
|
+
if (quoted) {
|
|
229
|
+
if (escaped) escaped = false;
|
|
230
|
+
else if (character === "\\") escaped = true;
|
|
231
|
+
else if (character === '"') quoted = false;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (character === '"') quoted = true;
|
|
235
|
+
else if (character === "{") depth += 1;
|
|
236
|
+
else if (character === "}") {
|
|
237
|
+
depth -= 1;
|
|
238
|
+
if (depth === 0) break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (depth !== 0) throw new Error("go command returned incomplete JSON");
|
|
242
|
+
records.push(JSON.parse(source.slice(start, end + 1)));
|
|
243
|
+
start = end + 1;
|
|
244
|
+
}
|
|
245
|
+
return records;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function snapshotPaths(paths) {
|
|
249
|
+
return paths.map((entry) => snapshotPath(entry));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function snapshotPath(entry) {
|
|
253
|
+
const target = path.resolve(entry);
|
|
254
|
+
let stat;
|
|
255
|
+
try {
|
|
256
|
+
stat = fs.statSync(target);
|
|
257
|
+
} catch {
|
|
258
|
+
throw new Error(`cache artifact is missing: ${target}`);
|
|
259
|
+
}
|
|
260
|
+
if (stat.isFile())
|
|
261
|
+
return { path: target, type: "file", sha256: fileDigest(target) };
|
|
262
|
+
if (!stat.isDirectory())
|
|
263
|
+
throw new Error(`cache artifact is not a file or directory: ${target}`);
|
|
264
|
+
const files = new Set();
|
|
265
|
+
addDirectory(files, target);
|
|
266
|
+
return {
|
|
267
|
+
path: target,
|
|
268
|
+
type: "directory",
|
|
269
|
+
files: [...files].sort().map((file) => ({
|
|
270
|
+
path: path.relative(target, file),
|
|
271
|
+
sha256: fileDigest(file),
|
|
272
|
+
})),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function addDirectory(files, directory) {
|
|
277
|
+
const root = path.resolve(directory);
|
|
278
|
+
let stat;
|
|
279
|
+
try {
|
|
280
|
+
stat = fs.statSync(root);
|
|
281
|
+
} catch {
|
|
282
|
+
throw new Error(`cache input directory is missing: ${root}`);
|
|
283
|
+
}
|
|
284
|
+
if (!stat.isDirectory())
|
|
285
|
+
throw new Error(`cache input is not a directory: ${root}`);
|
|
286
|
+
const stack = [root];
|
|
287
|
+
while (stack.length) {
|
|
288
|
+
const current = stack.pop();
|
|
289
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
290
|
+
const file = path.join(current, entry.name);
|
|
291
|
+
if (entry.isDirectory()) stack.push(file);
|
|
292
|
+
else if (entry.isFile()) files.add(file);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function addIfFile(files, file) {
|
|
298
|
+
try {
|
|
299
|
+
if (fs.statSync(file).isFile()) files.add(file);
|
|
300
|
+
} catch {
|
|
301
|
+
// A module can be in the graph without a colocated go.sum. Its go.mod is
|
|
302
|
+
// still tracked; absent optional sums must not make every cache lookup fail.
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function readRecord(cachePath) {
|
|
307
|
+
try {
|
|
308
|
+
const record = JSON.parse(fs.readFileSync(cachePath, "utf8"));
|
|
309
|
+
return record?.schema === 1 ? record : null;
|
|
310
|
+
} catch {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function writeAtomically(destination, content) {
|
|
316
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
317
|
+
const temporary = `${destination}.${process.pid}.${Date.now()}.tmp`;
|
|
318
|
+
fs.writeFileSync(temporary, content);
|
|
319
|
+
fs.rmSync(destination, { force: true });
|
|
320
|
+
fs.renameSync(temporary, destination);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function normalizedPath(file, cwd) {
|
|
324
|
+
const relative = path.relative(cwd, file);
|
|
325
|
+
return (relative.startsWith("..") ? path.resolve(file) : relative).replaceAll(
|
|
326
|
+
path.sep,
|
|
327
|
+
"/",
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function fileDigest(file) {
|
|
332
|
+
return digest(fs.readFileSync(file));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function digest(value) {
|
|
336
|
+
return crypto.createHash("sha256").update(value).digest("hex");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function stableJson(value) {
|
|
340
|
+
return JSON.stringify(value);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
module.exports = {
|
|
344
|
+
createGoBuildCache,
|
|
345
|
+
createInputIdentity,
|
|
346
|
+
parseJsonStream,
|
|
347
|
+
};
|
package/build/pack-prepare.cjs
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
// build-wasm.cjs emits a rewritten `dist/go.mod` that points at the
|
|
10
10
|
// vendored `./shim-vendor/shim/*` tree. Pack-prepare swaps the working-tree
|
|
11
11
|
// `go.mod` for that copy just before pack, then restores the original
|
|
12
|
-
// afterwards. The original is stashed in
|
|
13
|
-
// interrupted pack can be
|
|
12
|
+
// afterwards. The original is stashed in
|
|
13
|
+
// `node_modules/.cache/ttsc-wasm/go.mod.stash` so an interrupted pack can be
|
|
14
|
+
// recovered by running `--restore`.
|
|
14
15
|
//
|
|
15
16
|
// Usage:
|
|
16
17
|
// node build/pack-prepare.cjs --stash # prepack: stash + swap
|
package/cmd/ttsc-wasm/main.go
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// Command ttsc-wasm is the non-wasm sanity entrypoint for `@ttsc/wasm`.
|
|
4
4
|
//
|
|
5
|
-
// The
|
|
6
|
-
//
|
|
5
|
+
// The package has a separate GOOS=js GOARCH=wasm browser entry consumed by
|
|
6
|
+
// `bootTtsc`. For native targets we expose a minimal CLI so
|
|
7
7
|
// `go run ./cmd/ttsc-wasm` exercises the same host helpers without needing
|
|
8
8
|
// the browser MemFS bridge.
|
|
9
9
|
package main
|
|
@@ -55,39 +55,51 @@ func run(args []string) int {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
// runBuild invokes host.Build and writes the JSON result to stdout.
|
|
58
|
+
// runBuild invokes host.Build and writes the JSON result to stdout. A write
|
|
59
|
+
// failure returns code 3 even when compilation itself succeeded.
|
|
59
60
|
func runBuild(args []string) int {
|
|
60
61
|
cwd, tsconfig := parseProject(args)
|
|
61
62
|
data, code, err := host.Build(cwd, tsconfig)
|
|
62
63
|
if err != nil {
|
|
63
64
|
fmt.Fprintf(os.Stderr, "ttsc-wasm build: %v\n", err)
|
|
64
|
-
return
|
|
65
|
+
return code
|
|
66
|
+
}
|
|
67
|
+
if _, err := os.Stdout.Write(append(data, '\n')); err != nil {
|
|
68
|
+
fmt.Fprintf(os.Stderr, "ttsc-wasm build: write result: %v\n", err)
|
|
69
|
+
return 3
|
|
65
70
|
}
|
|
66
|
-
_, _ = os.Stdout.Write(append(data, '\n'))
|
|
67
71
|
return code
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
// runCheck invokes host.Check and writes the JSON result to stdout.
|
|
74
|
+
// runCheck invokes host.Check and writes the JSON result to stdout. Code 3
|
|
75
|
+
// reports an output failure independently of the compiler diagnostic status.
|
|
71
76
|
func runCheck(args []string) int {
|
|
72
77
|
cwd, tsconfig := parseProject(args)
|
|
73
78
|
data, code, err := host.Check(cwd, tsconfig)
|
|
74
79
|
if err != nil {
|
|
75
80
|
fmt.Fprintf(os.Stderr, "ttsc-wasm check: %v\n", err)
|
|
76
|
-
return
|
|
81
|
+
return code
|
|
82
|
+
}
|
|
83
|
+
if _, err := os.Stdout.Write(append(data, '\n')); err != nil {
|
|
84
|
+
fmt.Fprintf(os.Stderr, "ttsc-wasm check: write result: %v\n", err)
|
|
85
|
+
return 3
|
|
77
86
|
}
|
|
78
|
-
_, _ = os.Stdout.Write(append(data, '\n'))
|
|
79
87
|
return code
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
// runTransform invokes host.Transform and writes the JSON result to stdout.
|
|
91
|
+
// Code 3 reports that the source envelope could not be written.
|
|
83
92
|
func runTransform(args []string) int {
|
|
84
93
|
cwd, tsconfig := parseProject(args)
|
|
85
94
|
data, code, err := host.Transform(cwd, tsconfig)
|
|
86
95
|
if err != nil {
|
|
87
96
|
fmt.Fprintf(os.Stderr, "ttsc-wasm transform: %v\n", err)
|
|
88
|
-
return
|
|
97
|
+
return code
|
|
98
|
+
}
|
|
99
|
+
if _, err := os.Stdout.Write(append(data, '\n')); err != nil {
|
|
100
|
+
fmt.Fprintf(os.Stderr, "ttsc-wasm transform: write result: %v\n", err)
|
|
101
|
+
return 3
|
|
89
102
|
}
|
|
90
|
-
_, _ = os.Stdout.Write(append(data, '\n'))
|
|
91
103
|
return code
|
|
92
104
|
}
|
|
93
105
|
|
|
@@ -10,8 +10,8 @@ import (
|
|
|
10
10
|
"github.com/samchon/ttsc/packages/wasm/host"
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
// main installs the base ttsc API on globalThis
|
|
14
|
-
//
|
|
13
|
+
// main installs the base ttsc API on globalThis. A valid first registration
|
|
14
|
+
// keeps the wasm runtime alive until its Worker is terminated.
|
|
15
15
|
func main() {
|
|
16
16
|
host.Expose("ttsc", host.Config{
|
|
17
17
|
Plugins: nil,
|