ttsc 0.23.0 → 0.25.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 -21
- package/README.md +66 -63
- package/cmd/ttscgraph/serve.go +264 -48
- package/cmd/ttscgraph/serve_answers_a_project_that_implements_its_own_ambient_declaration_test.go +69 -0
- package/cmd/ttscgraph/serve_early_error_encode_failure_is_observable_test.go +40 -0
- package/cmd/ttscgraph/serve_graph_snapshot_version_matches_typescript_client_test.go +32 -0
- package/cmd/ttscgraph/serve_phase_trace_accounts_for_load_failure_test.go +45 -0
- package/cmd/ttscgraph/serve_phase_trace_is_opt_in_and_payload_free_test.go +52 -0
- package/cmd/ttscgraph/serve_protocol_negotiates_incremental_shards_test.go +44 -0
- package/cmd/ttscgraph/serve_session_reloads_commonjs_package_entry_for_esm_test.go +2 -2
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +46 -20
- package/cmd/ttscgraph/serve_session_retries_failed_dump_test.go +9 -9
- package/cmd/ttscgraph/serve_shards.go +1390 -0
- package/cmd/ttscgraph/serve_shards_abandoned_generation_converges_on_retry_test.go +92 -0
- package/cmd/ttscgraph/serve_shards_delete_removed_source_test.go +57 -0
- package/cmd/ttscgraph/serve_shards_external_endpoints_test.go +53 -0
- package/cmd/ttscgraph/serve_shards_keep_source_distributed_dependencies_at_the_boundary_test.go +108 -0
- package/cmd/ttscgraph/serve_shards_match_full_dump_oracle_test.go +88 -0
- package/cmd/ttscgraph/serve_shards_merge_pending_and_new_edits_test.go +50 -0
- package/cmd/ttscgraph/serve_shards_prune_unreferenced_external_endpoints_test.go +65 -0
- package/cmd/ttscgraph/serve_shards_publish_created_source_test.go +92 -0
- package/cmd/ttscgraph/serve_shards_rebuild_declaration_edit_test.go +43 -0
- package/cmd/ttscgraph/serve_shards_refresh_cross_file_implementation_test.go +63 -0
- package/cmd/ttscgraph/serve_shards_refresh_dependent_diagnostics_test.go +80 -0
- package/cmd/ttscgraph/serve_shards_rename_moves_source_identity_test.go +95 -0
- package/cmd/ttscgraph/serve_shards_retry_preserves_committed_generation_test.go +48 -0
- package/cmd/ttscgraph/serve_shards_reuse_unaffected_sources_test.go +121 -0
- package/driver/declaration_shape.go +85 -0
- package/driver/program.go +39 -10
- package/go.sum +22 -22
- package/internal/graph/assigned_implementations_of_declaration_file_symbols_leave_no_edge_source_test.go +84 -0
- package/internal/graph/assigned_implementations_of_node_modules_symbols_leave_no_edge_source_test.go +85 -0
- package/internal/graph/build.go +62 -10
- package/internal/graph/build_files_reuses_committed_cross_file_endpoints_test.go +87 -0
- package/internal/graph/build_keeps_source_distributed_dependencies_at_the_external_boundary_test.go +74 -0
- package/internal/graph/dump.go +84 -43
- package/internal/graph/dump_path_mapper_caches_alias_resolution_test.go +29 -0
- package/internal/graph/dump_paths.go +134 -11
- package/internal/graph/dump_paths_canonicalize_windows_short_root_test.go +39 -0
- package/internal/graph/dump_rejects_provenance_only_path_errors_test.go +32 -0
- package/internal/graph/dump_serializes_the_full_graph_test.go +2 -2
- package/internal/graph/dump_symlink_project_publishes_matching_base_test.go +82 -0
- package/internal/graph/edges.go +73 -17
- package/internal/graph/exports.go +9 -2
- package/internal/graph/graph.go +47 -7
- package/internal/graph/graph_kind_contracts_match_their_producers_test.go +121 -0
- package/internal/graph/node_modifiers_emit_union_strings_test.go +1 -1
- package/internal/graph/provenance.go +11 -1
- package/internal/graph/resolve.go +1 -4
- package/internal/graph/source.go +23 -0
- package/internal/graph/wire_node_ids_share_alias_resolution_test.go +29 -0
- package/internal/lspserver/lsp_native_plugin_source.go +5 -6
- package/internal/lspserver/lsp_native_resident.go +17 -24
- package/lib/TtscService.d.ts +1 -6
- package/lib/TtscService.js +2 -2
- package/lib/TtscService.js.map +1 -1
- package/lib/compiler/internal/captureProcessOutput.d.ts +26 -0
- package/lib/compiler/internal/captureProcessOutput.js +92 -0
- package/lib/compiler/internal/captureProcessOutput.js.map +1 -0
- package/lib/compiler/internal/compileProjectInMemory.js +4 -7
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/residentCheckProcess.d.ts +0 -2
- package/lib/compiler/internal/residentCheckProcess.js +1 -15
- package/lib/compiler/internal/residentCheckProcess.js.map +1 -1
- package/lib/compiler/internal/residentTransformProcess.d.ts +0 -6
- package/lib/compiler/internal/residentTransformProcess.js +0 -25
- package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +14 -6
- package/lib/compiler/internal/spawnNative.js +32 -11
- package/lib/compiler/internal/spawnNative.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.d.ts +1 -5
- package/lib/compiler/internal/startResidentTransform.js +1 -3
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.d.ts +2 -1
- package/lib/launcher/internal/prepareExecution.js +269 -23
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/registerRuntimeHooks.js +6 -1
- package/lib/launcher/internal/registerRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +4 -9
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +2 -55
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +25 -2
- package/lib/launcher/internal/runtimeHooks.js +344 -69
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.js +31 -3
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/descriptorProcessFailure.d.ts +23 -19
- package/lib/plugin/internal/descriptorProcessFailure.js +40 -51
- package/lib/plugin/internal/descriptorProcessFailure.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +12 -0
- package/lib/plugin/internal/loadProjectPlugins.js +77 -28
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/package.json +8 -8
- package/shim/ast/extra-shim.json +4 -4
- package/shim/ast/go.mod +14 -14
- package/shim/ast/go.sum +20 -20
- package/shim/astnav/go.mod +14 -14
- package/shim/astnav/go.sum +20 -20
- package/shim/bundled/extra-shim.json +1 -1
- package/shim/bundled/go.mod +5 -5
- package/shim/checker/extra-shim.json +31 -31
- package/shim/checker/go.mod +5 -5
- package/shim/compiler/extra-shim.json +4 -4
- package/shim/compiler/go.mod +14 -14
- package/shim/compiler/go.sum +22 -22
- package/shim/core/extra-shim.json +4 -4
- package/shim/core/go.mod +5 -5
- package/shim/diagnosticwriter/go.mod +5 -5
- package/shim/lsp/extra-shim.json +4 -4
- package/shim/lsp/go.mod +16 -16
- package/shim/lsp/go.sum +26 -26
- package/shim/parser/extra-shim.json +4 -4
- package/shim/parser/go.mod +5 -5
- package/shim/printer/go.mod +14 -14
- package/shim/printer/go.sum +22 -22
- package/shim/scanner/extra-shim.json +4 -4
- package/shim/scanner/go.mod +5 -5
- package/shim/transformers/go.mod +14 -14
- package/shim/transformers/go.sum +20 -20
- package/shim/tsoptions/extra-shim.json +4 -4
- package/shim/tsoptions/go.mod +5 -5
- package/shim/tspath/extra-shim.json +4 -4
- package/shim/tspath/go.mod +5 -5
- package/shim/vfs/cachedvfs/extra-shim.json +4 -4
- package/shim/vfs/cachedvfs/go.mod +5 -5
- package/shim/vfs/extra-shim.json +4 -4
- package/shim/vfs/go.mod +5 -5
- package/shim/vfs/osvfs/extra-shim.json +4 -4
- package/shim/vfs/osvfs/go.mod +5 -5
- package/src/TtscService.ts +8 -20
- package/src/compiler/internal/captureProcessOutput.ts +96 -0
- package/src/compiler/internal/compileProjectInMemory.ts +7 -7
- package/src/compiler/internal/residentCheckProcess.ts +1 -21
- package/src/compiler/internal/residentTransformProcess.ts +0 -42
- package/src/compiler/internal/spawnNative.ts +39 -18
- package/src/compiler/internal/startResidentTransform.ts +1 -12
- package/src/launcher/internal/prepareExecution.ts +297 -26
- package/src/launcher/internal/registerRuntimeHooks.ts +11 -2
- package/src/launcher/internal/runTtscserver.ts +4 -9
- package/src/launcher/internal/runTtsx.ts +2 -78
- package/src/launcher/internal/runtimeHooks.ts +385 -82
- package/src/plugin/internal/buildSourcePlugin.ts +36 -3
- package/src/plugin/internal/descriptorProcessFailure.ts +34 -65
- package/src/plugin/internal/loadProjectPlugins.ts +78 -39
- package/tools/gen_shims/go.mod +14 -14
- package/tools/gen_shims/go.sum +12 -12
- package/tools/shim_audit/go.mod +10 -10
- package/tools/shim_audit/go.sum +8 -8
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type SpawnSyncOptionsWithStringEncoding,
|
|
3
3
|
type SpawnSyncReturns,
|
|
4
|
+
type StdioOptions,
|
|
4
5
|
spawnSync,
|
|
5
6
|
} from "node:child_process";
|
|
6
7
|
import crypto from "node:crypto";
|
|
@@ -10,6 +11,7 @@ import os from "node:os";
|
|
|
10
11
|
import path from "node:path";
|
|
11
12
|
import { fileURLToPath } from "node:url";
|
|
12
13
|
|
|
14
|
+
import { captureProcessOutput } from "../../compiler/internal/captureProcessOutput";
|
|
13
15
|
import { findNearestGoMod } from "../../compiler/internal/paths";
|
|
14
16
|
|
|
15
17
|
const GO_MOD_SEARCH_MAX_DEPTH = 3;
|
|
@@ -1646,7 +1648,6 @@ function readGoModInfo(
|
|
|
1646
1648
|
cwd: dir,
|
|
1647
1649
|
encoding: "utf8",
|
|
1648
1650
|
env: goBuildEnv(goBinary, undefined, env),
|
|
1649
|
-
maxBuffer: 1024 * 1024 * 16,
|
|
1650
1651
|
windowsHide: true,
|
|
1651
1652
|
});
|
|
1652
1653
|
if (result.error) {
|
|
@@ -1737,7 +1738,6 @@ function runGoBuild(
|
|
|
1737
1738
|
cwd,
|
|
1738
1739
|
encoding: "utf8",
|
|
1739
1740
|
env: goBuildEnv(goBinary, goBuildCacheRoot, env),
|
|
1740
|
-
maxBuffer: 1024 * 1024 * 64,
|
|
1741
1741
|
windowsHide: true,
|
|
1742
1742
|
});
|
|
1743
1743
|
if (result.error) {
|
|
@@ -1767,6 +1767,40 @@ export function spawnGoTool(
|
|
|
1767
1767
|
goBinary: string,
|
|
1768
1768
|
args: readonly string[],
|
|
1769
1769
|
options: SpawnSyncOptionsWithStringEncoding,
|
|
1770
|
+
): SpawnSyncReturns<string> {
|
|
1771
|
+
// The child's streams go to files rather than pipes, so no output ceiling
|
|
1772
|
+
// applies: `spawnSync` only bounds what it must hold in this process's
|
|
1773
|
+
// memory, and a `go build` that says a great deal is not a failure to invent
|
|
1774
|
+
// a limit for. See `captureProcessOutput`.
|
|
1775
|
+
const capture = captureProcessOutput();
|
|
1776
|
+
const spawnOptions = {
|
|
1777
|
+
...options,
|
|
1778
|
+
stdio: ["ignore", capture.stdoutFd, capture.stderrFd] as StdioOptions,
|
|
1779
|
+
};
|
|
1780
|
+
try {
|
|
1781
|
+
const result = spawnGoToolProcess(goBinary, args, spawnOptions);
|
|
1782
|
+
const stdout = capture.read("stdout", "utf8") as string;
|
|
1783
|
+
const stderr = capture.read("stderr", "utf8") as string;
|
|
1784
|
+
return {
|
|
1785
|
+
...result,
|
|
1786
|
+
output: [null, stdout, stderr],
|
|
1787
|
+
stderr,
|
|
1788
|
+
stdout,
|
|
1789
|
+
};
|
|
1790
|
+
} finally {
|
|
1791
|
+
capture.dispose();
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
/**
|
|
1796
|
+
* Launch the Go tool, routing through cmd.exe only where Windows needs a
|
|
1797
|
+
* wrapper. Output routing is the caller's concern; this owns process
|
|
1798
|
+
* selection.
|
|
1799
|
+
*/
|
|
1800
|
+
function spawnGoToolProcess(
|
|
1801
|
+
goBinary: string,
|
|
1802
|
+
args: readonly string[],
|
|
1803
|
+
options: SpawnSyncOptionsWithStringEncoding,
|
|
1770
1804
|
): SpawnSyncReturns<string> {
|
|
1771
1805
|
if (process.platform !== "win32") {
|
|
1772
1806
|
return spawnSync(goBinary, [...args], options);
|
|
@@ -2734,7 +2768,6 @@ function resolveGoBuildEnvironment(
|
|
|
2734
2768
|
cwd,
|
|
2735
2769
|
encoding: "utf8",
|
|
2736
2770
|
env: goBuildEnv(goBinary, undefined, env),
|
|
2737
|
-
maxBuffer: 1024 * 1024,
|
|
2738
2771
|
windowsHide: true,
|
|
2739
2772
|
},
|
|
2740
2773
|
);
|
|
@@ -1,50 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
export const PLUGIN_DESCRIPTOR_TIMEOUT_MS = 60_000;
|
|
3
|
-
export const PLUGIN_DESCRIPTOR_TEARDOWN_GRACE_MS = 5_000;
|
|
4
|
-
export const PLUGIN_DESCRIPTOR_STATUS_FD = 3;
|
|
5
|
-
export const PLUGIN_DESCRIPTOR_PROCESS_OPTIONS = Object.freeze({
|
|
6
|
-
killSignal: "SIGKILL" as const,
|
|
7
|
-
maxBuffer: PLUGIN_DESCRIPTOR_MAX_BUFFER_BYTES,
|
|
8
|
-
timeout: PLUGIN_DESCRIPTOR_TIMEOUT_MS + PLUGIN_DESCRIPTOR_TEARDOWN_GRACE_MS,
|
|
9
|
-
});
|
|
1
|
+
import fs from "node:fs";
|
|
10
2
|
|
|
11
3
|
interface DescriptorProcessResult {
|
|
12
4
|
error?: Error;
|
|
13
|
-
output?: readonly (string | null)[] | null;
|
|
14
5
|
signal: NodeJS.Signals | null;
|
|
15
6
|
status: number | null;
|
|
16
|
-
stderr: string | null | undefined;
|
|
17
|
-
stdout: string | null | undefined;
|
|
18
7
|
}
|
|
19
8
|
|
|
20
9
|
/**
|
|
21
10
|
* Classify the ways the isolated TypeScript descriptor evaluator can stop.
|
|
22
11
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
12
|
+
* The loader writes the child's own output straight to this process's stderr,
|
|
13
|
+
* so a diagnostic has already reached the user by the time anything here runs.
|
|
14
|
+
* What is left to say is only how the process ended: it never launched,
|
|
15
|
+
* something outside killed it, or it exited non-zero after printing its own
|
|
16
|
+
* reason.
|
|
17
|
+
*
|
|
18
|
+
* Nothing is bounded here — not time, not output. Both were the compiler
|
|
19
|
+
* deciding, on numbers nobody chose for this machine, that a user's own
|
|
20
|
+
* descriptor had run too long or said too much. Neither is this process's
|
|
21
|
+
* memory to spend either, because the child's streams are no longer collected
|
|
22
|
+
* into it.
|
|
27
23
|
*/
|
|
28
24
|
export function pluginDescriptorProcessFailure(
|
|
29
25
|
result: DescriptorProcessResult,
|
|
30
26
|
request: string,
|
|
31
27
|
): Error | undefined {
|
|
32
|
-
const code = (result.error as NodeJS.ErrnoException | undefined)?.code;
|
|
33
|
-
const nestedCode = result.output?.[PLUGIN_DESCRIPTOR_STATUS_FD]?.trim() ?? "";
|
|
34
|
-
if (code === "ETIMEDOUT" || nestedCode === "ETIMEDOUT") {
|
|
35
|
-
return new Error(
|
|
36
|
-
`ttsc: plugin descriptor "${request}" evaluation through ttsx timed out after ` +
|
|
37
|
-
`${PLUGIN_DESCRIPTOR_TIMEOUT_MS / 1_000} seconds. ` +
|
|
38
|
-
"Descriptor modules and factories must finish their setup within that window.",
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
if (code === "ENOBUFS" || nestedCode === "ENOBUFS") {
|
|
42
|
-
return new Error(
|
|
43
|
-
`ttsc: plugin descriptor "${request}" evaluation through ttsx exceeded the ` +
|
|
44
|
-
`${PLUGIN_DESCRIPTOR_MAX_BUFFER_BYTES / (1024 * 1024)} MiB process output limit. ` +
|
|
45
|
-
"Reduce stdout and stderr from the descriptor and its dependencies.",
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
28
|
if (result.error) {
|
|
49
29
|
return new Error(
|
|
50
30
|
`ttsc: failed to launch ttsx for plugin descriptor "${request}": ${result.error.message}`,
|
|
@@ -56,45 +36,34 @@ export function pluginDescriptorProcessFailure(
|
|
|
56
36
|
);
|
|
57
37
|
}
|
|
58
38
|
if (result.status !== 0) {
|
|
59
|
-
const reason = descriptorFailureReason(
|
|
60
|
-
hasText(result.stderr) ? result.stderr : result.stdout,
|
|
61
|
-
);
|
|
62
39
|
return new Error(
|
|
63
|
-
`ttsc: plugin descriptor "${request}" evaluation through ttsx failed with exit code ${String(result.status)}
|
|
64
|
-
(reason === "" ? "" : "\n" + reason),
|
|
40
|
+
`ttsc: plugin descriptor "${request}" evaluation through ttsx failed with exit code ${String(result.status)}`,
|
|
65
41
|
);
|
|
66
42
|
}
|
|
67
43
|
return undefined;
|
|
68
44
|
}
|
|
69
45
|
|
|
70
46
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
47
|
+
* Read the failure envelope the descriptor shim writes to its result file when
|
|
48
|
+
* it stops on an error it can name.
|
|
49
|
+
*
|
|
50
|
+
* This is the other half of classifying how the evaluation ended, which is why
|
|
51
|
+
* it lives beside {@link pluginDescriptorProcessFailure} rather than at the call
|
|
52
|
+
* site: the status says that it failed, and this says why.
|
|
53
|
+
*
|
|
54
|
+
* Only a well-formed envelope is honoured. A real descriptor never carries this
|
|
55
|
+
* key, and every other shape — an absent file, a shim that died before writing,
|
|
56
|
+
* a half-written result, a descriptor written before a later non-zero exit —
|
|
57
|
+
* leaves the process status to speak for itself.
|
|
73
58
|
*/
|
|
74
|
-
export function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function hasText(value: string | null | undefined): value is string {
|
|
87
|
-
return value !== null && value !== undefined && value.trim() !== "";
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function descriptorFailureReason(stderr: string | null | undefined): string {
|
|
91
|
-
const bounded = (stderr ?? "").slice(-PLUGIN_DESCRIPTOR_REASON_MAX_CHARS);
|
|
92
|
-
const lines = bounded
|
|
93
|
-
.split(/\r?\n/)
|
|
94
|
-
.map((line) => line.trimEnd())
|
|
95
|
-
.filter((line) => line.trim() !== "");
|
|
96
|
-
return lines.slice(-PLUGIN_DESCRIPTOR_REASON_LINES).join("\n");
|
|
59
|
+
export function pluginDescriptorFailureReason(outputPath: string): string {
|
|
60
|
+
try {
|
|
61
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(outputPath, "utf8"));
|
|
62
|
+
if (typeof parsed !== "object" || parsed === null) return "";
|
|
63
|
+
const message = (parsed as { __ttscLoaderError?: unknown })
|
|
64
|
+
.__ttscLoaderError;
|
|
65
|
+
return typeof message === "string" ? message.trim() : "";
|
|
66
|
+
} catch {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
97
69
|
}
|
|
98
|
-
|
|
99
|
-
const PLUGIN_DESCRIPTOR_REASON_LINES = 5;
|
|
100
|
-
const PLUGIN_DESCRIPTOR_REASON_MAX_CHARS = 8 * 1024;
|
|
@@ -16,10 +16,7 @@ import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoa
|
|
|
16
16
|
import type { ITtscParsedProjectConfig } from "../../structures/internal/ITtscParsedProjectConfig";
|
|
17
17
|
import { buildSourcePlugin } from "./buildSourcePlugin";
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
PLUGIN_DESCRIPTOR_PROCESS_OPTIONS,
|
|
21
|
-
PLUGIN_DESCRIPTOR_STATUS_FD,
|
|
22
|
-
pluginDescriptorBoundaryEnvironment,
|
|
19
|
+
pluginDescriptorFailureReason,
|
|
23
20
|
pluginDescriptorProcessFailure,
|
|
24
21
|
} from "./descriptorProcessFailure";
|
|
25
22
|
|
|
@@ -696,6 +693,50 @@ function requirePluginEntry(
|
|
|
696
693
|
|
|
697
694
|
const TS_SOURCE_PATTERN = /\.(?:[cm]?ts|tsx)$/i;
|
|
698
695
|
|
|
696
|
+
/**
|
|
697
|
+
* The descriptor shim's emitted source.
|
|
698
|
+
*
|
|
699
|
+
* Exported so a regression can inspect the same bytes ttsx executes. This
|
|
700
|
+
* template consumes its own escapes, so reading this file's text instead would
|
|
701
|
+
* check characters no consumer ever sees — and a dropped backslash turns an
|
|
702
|
+
* escape into the character it was escaping: a raw line terminator inside a
|
|
703
|
+
* string literal, which stops the shim parsing and takes every descriptor load
|
|
704
|
+
* with it. Its `@ttsc/lint` twin has carried that guard since the same defect
|
|
705
|
+
* shipped there.
|
|
706
|
+
*/
|
|
707
|
+
export const PLUGIN_DESCRIPTOR_SHIM_SOURCE = [
|
|
708
|
+
`// @ts-nocheck`,
|
|
709
|
+
`import { writeFileSync } from "node:fs";`,
|
|
710
|
+
`import { pathToFileURL } from "node:url";`,
|
|
711
|
+
// The import is inside the try, not above it. A descriptor that cannot be
|
|
712
|
+
// found, or whose module body throws, fails exactly where a descriptor
|
|
713
|
+
// whose factory throws does, and a caller deserves the same reason for
|
|
714
|
+
// both — "Cannot find module ./missing" is as actionable as anything the
|
|
715
|
+
// factory could have said.
|
|
716
|
+
`try {`,
|
|
717
|
+
` const mod = await import(pathToFileURL(process.env.TTSC_PLUGIN_ENTRY).href);`,
|
|
718
|
+
` const context = JSON.parse(process.env.TTSC_PLUGIN_CONTEXT);`,
|
|
719
|
+
` const candidate = mod.createTtscPlugin ?? mod.default ?? mod.plugin ?? mod;`,
|
|
720
|
+
` const descriptor =`,
|
|
721
|
+
` typeof candidate === "function" ? candidate(context) : candidate;`,
|
|
722
|
+
` writeFileSync(process.env.TTSC_PLUGIN_DESCRIPTOR_OUT, JSON.stringify(descriptor));`,
|
|
723
|
+
`} catch (error) {`,
|
|
724
|
+
// The stack streams to the user's stderr on its own. This puts the reason
|
|
725
|
+
// a caller can act on into the channel the parent already reads, so the
|
|
726
|
+
// failure is not reduced to a bare exit status.
|
|
727
|
+
// The escape is doubled on purpose: this template consumes one level, so
|
|
728
|
+
// `\\n` here is what puts the two-character escape into the emitted shim.
|
|
729
|
+
// A single `\n` would put a raw line terminator inside a string literal,
|
|
730
|
+
// and the shim would stop parsing — taking every descriptor load with it.
|
|
731
|
+
` process.stderr.write((error instanceof Error && error.stack ? error.stack : String(error)) + "\\n");`,
|
|
732
|
+
` try {`,
|
|
733
|
+
` writeFileSync(process.env.TTSC_PLUGIN_DESCRIPTOR_OUT, JSON.stringify({ __ttscLoaderError: error instanceof Error ? error.message : String(error) }));`,
|
|
734
|
+
` } catch {}`,
|
|
735
|
+
` process.exit(1);`,
|
|
736
|
+
`}`,
|
|
737
|
+
``,
|
|
738
|
+
].join("\n");
|
|
739
|
+
|
|
699
740
|
/**
|
|
700
741
|
* Evaluate a `.ts` plugin descriptor entry in a child `ttsx` process and return
|
|
701
742
|
* the descriptor it produces. A generated shim imports the entry, invokes its
|
|
@@ -735,21 +776,7 @@ function loadDescriptorViaTtsx(
|
|
|
735
776
|
},
|
|
736
777
|
}),
|
|
737
778
|
);
|
|
738
|
-
fs.writeFileSync(
|
|
739
|
-
shim,
|
|
740
|
-
[
|
|
741
|
-
`// @ts-nocheck`,
|
|
742
|
-
`import { writeFileSync } from "node:fs";`,
|
|
743
|
-
`import { pathToFileURL } from "node:url";`,
|
|
744
|
-
`const mod = await import(pathToFileURL(process.env.TTSC_PLUGIN_ENTRY).href);`,
|
|
745
|
-
`const context = JSON.parse(process.env.TTSC_PLUGIN_CONTEXT);`,
|
|
746
|
-
`const candidate = mod.createTtscPlugin ?? mod.default ?? mod.plugin ?? mod;`,
|
|
747
|
-
`const descriptor =`,
|
|
748
|
-
` typeof candidate === "function" ? candidate(context) : candidate;`,
|
|
749
|
-
`writeFileSync(process.env.TTSC_PLUGIN_DESCRIPTOR_OUT, JSON.stringify(descriptor));`,
|
|
750
|
-
``,
|
|
751
|
-
].join("\n"),
|
|
752
|
-
);
|
|
779
|
+
fs.writeFileSync(shim, PLUGIN_DESCRIPTOR_SHIM_SOURCE);
|
|
753
780
|
try {
|
|
754
781
|
const result = childProcess.spawnSync(node, [ttsx, "--no-plugins", shim], {
|
|
755
782
|
cwd: context.projectRoot,
|
|
@@ -773,34 +800,30 @@ function loadDescriptorViaTtsx(
|
|
|
773
800
|
TTSC_PLUGIN_DESCRIPTOR_LOAD: "1",
|
|
774
801
|
TTSC_PLUGIN_DESCRIPTOR_OUT: out,
|
|
775
802
|
TTSC_PLUGIN_ENTRY: request,
|
|
776
|
-
...pluginDescriptorBoundaryEnvironment(),
|
|
777
803
|
},
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
...Array.from(
|
|
784
|
-
{ length: PLUGIN_DESCRIPTOR_STATUS_FD - 2 },
|
|
785
|
-
() => "pipe" as const,
|
|
786
|
-
),
|
|
787
|
-
],
|
|
804
|
+
// Both child streams are human output, and they go straight to this
|
|
805
|
+
// process's stderr as they are written. The descriptor itself travels
|
|
806
|
+
// through a file, so nothing here needs collecting — and collecting it
|
|
807
|
+
// only to replay it afterwards is what forced an invented output ceiling.
|
|
808
|
+
stdio: ["ignore", 2, 2],
|
|
788
809
|
windowsHide: true,
|
|
789
810
|
});
|
|
790
811
|
const processFailure = pluginDescriptorProcessFailure(result, request);
|
|
791
|
-
if (processFailure)
|
|
812
|
+
if (processFailure) {
|
|
813
|
+
// The descriptor's stack already reached the user's stderr as it ran.
|
|
814
|
+
// What it could not put there is a reason a caller can act on, so that
|
|
815
|
+
// arrives through the result file instead.
|
|
816
|
+
const reason = pluginDescriptorFailureReason(out);
|
|
817
|
+
throw reason === ""
|
|
818
|
+
? processFailure
|
|
819
|
+
: new Error(`${processFailure.message}
|
|
820
|
+
${reason}`);
|
|
821
|
+
}
|
|
792
822
|
if (!fs.existsSync(out)) {
|
|
793
823
|
throw new Error(
|
|
794
824
|
`ttsc: plugin descriptor "${request}" evaluation through ttsx produced no descriptor output.`,
|
|
795
825
|
);
|
|
796
826
|
}
|
|
797
|
-
const outputSize = fs.statSync(out).size;
|
|
798
|
-
if (outputSize > PLUGIN_DESCRIPTOR_MAX_BUFFER_BYTES) {
|
|
799
|
-
throw new Error(
|
|
800
|
-
`ttsc: plugin descriptor "${request}" produced ${outputSize} bytes of JSON, ` +
|
|
801
|
-
`exceeding the ${PLUGIN_DESCRIPTOR_MAX_BUFFER_BYTES / (1024 * 1024)} MiB descriptor output limit.`,
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
827
|
const text = fs.readFileSync(out, "utf8");
|
|
805
828
|
try {
|
|
806
829
|
return JSON.parse(text);
|
|
@@ -810,7 +833,7 @@ function loadDescriptorViaTtsx(
|
|
|
810
833
|
);
|
|
811
834
|
}
|
|
812
835
|
} finally {
|
|
813
|
-
|
|
836
|
+
removeEvaluationTempDir(dir);
|
|
814
837
|
}
|
|
815
838
|
}
|
|
816
839
|
|
|
@@ -1389,3 +1412,19 @@ function readTsgoVersion(projectRoot: string): string {
|
|
|
1389
1412
|
return "unknown";
|
|
1390
1413
|
}
|
|
1391
1414
|
}
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Remove an evaluation temp directory without letting cleanup replace a result.
|
|
1418
|
+
*
|
|
1419
|
+
* This runs from a `finally`, so a throw here would surface instead of the
|
|
1420
|
+
* evaluation's own outcome — and on Windows a grandchild that inherited a
|
|
1421
|
+
* handle, or a scanner holding the file, can make removal fail. Leaving bytes
|
|
1422
|
+
* in the system temp directory is by far the lesser outcome.
|
|
1423
|
+
*/
|
|
1424
|
+
function removeEvaluationTempDir(directory: string): void {
|
|
1425
|
+
try {
|
|
1426
|
+
fs.rmSync(directory, { force: true, recursive: true });
|
|
1427
|
+
} catch {
|
|
1428
|
+
// Best effort.
|
|
1429
|
+
}
|
|
1430
|
+
}
|
package/tools/gen_shims/go.mod
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
module github.com/samchon/ttsc/packages/ttsc/tools/gen_shims
|
|
2
|
-
|
|
3
|
-
go 1.26
|
|
4
|
-
|
|
5
|
-
require (
|
|
6
|
-
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433
|
|
7
|
-
golang.org/x/text v0.35.0
|
|
8
|
-
golang.org/x/tools v0.43.0
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
require (
|
|
12
|
-
golang.org/x/mod v0.34.0 // indirect
|
|
13
|
-
golang.org/x/sync v0.20.0 // indirect
|
|
14
|
-
)
|
|
1
|
+
module github.com/samchon/ttsc/packages/ttsc/tools/gen_shims
|
|
2
|
+
|
|
3
|
+
go 1.26
|
|
4
|
+
|
|
5
|
+
require (
|
|
6
|
+
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433
|
|
7
|
+
golang.org/x/text v0.35.0
|
|
8
|
+
golang.org/x/tools v0.43.0
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
require (
|
|
12
|
+
golang.org/x/mod v0.34.0 // indirect
|
|
13
|
+
golang.org/x/sync v0.20.0 // indirect
|
|
14
|
+
)
|
package/tools/gen_shims/go.sum
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao=
|
|
2
|
-
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
|
|
3
|
-
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
4
|
-
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
5
|
-
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
|
6
|
-
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
|
7
|
-
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|
8
|
-
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
9
|
-
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
|
10
|
-
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
|
11
|
-
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
|
12
|
-
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
|
1
|
+
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao=
|
|
2
|
+
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
|
|
3
|
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
4
|
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
5
|
+
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
|
6
|
+
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
|
7
|
+
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|
8
|
+
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
9
|
+
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
|
10
|
+
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
|
11
|
+
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
|
12
|
+
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
package/tools/shim_audit/go.mod
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
module github.com/samchon/ttsc/packages/ttsc/tools/shim_audit
|
|
2
|
-
|
|
3
|
-
go 1.26
|
|
4
|
-
|
|
5
|
-
require golang.org/x/tools v0.43.0
|
|
6
|
-
|
|
7
|
-
require (
|
|
8
|
-
golang.org/x/mod v0.34.0 // indirect
|
|
9
|
-
golang.org/x/sync v0.20.0 // indirect
|
|
10
|
-
)
|
|
1
|
+
module github.com/samchon/ttsc/packages/ttsc/tools/shim_audit
|
|
2
|
+
|
|
3
|
+
go 1.26
|
|
4
|
+
|
|
5
|
+
require golang.org/x/tools v0.43.0
|
|
6
|
+
|
|
7
|
+
require (
|
|
8
|
+
golang.org/x/mod v0.34.0 // indirect
|
|
9
|
+
golang.org/x/sync v0.20.0 // indirect
|
|
10
|
+
)
|
package/tools/shim_audit/go.sum
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
2
|
-
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
3
|
-
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
|
4
|
-
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
|
5
|
-
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|
6
|
-
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
7
|
-
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
|
8
|
-
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
|
1
|
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
2
|
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
3
|
+
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
|
4
|
+
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
|
5
|
+
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|
6
|
+
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
7
|
+
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
|
8
|
+
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|