ttsc 0.16.2 → 0.16.5

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.
Files changed (81) hide show
  1. package/README.md +96 -25
  2. package/cmd/graphbench/main.go +135 -0
  3. package/cmd/graphdump/main.go +60 -0
  4. package/cmd/ttscgraph/dump.go +69 -0
  5. package/cmd/ttscgraph/main.go +91 -0
  6. package/cmd/ttscgraph/run_dump_prints_the_graph_as_json_test.go +80 -0
  7. package/cmd/ttscgraph/run_help_version_and_bad_flags_exit_codes_test.go +75 -0
  8. package/cmd/utility-host/main.go +4 -1
  9. package/driver/overlay.go +64 -0
  10. package/driver/program.go +8 -1
  11. package/driver/session.go +85 -0
  12. package/internal/graph/build.go +330 -0
  13. package/internal/graph/build_records_a_node_per_top_level_declaration_test.go +96 -0
  14. package/internal/graph/decorators.go +122 -0
  15. package/internal/graph/decorators_capture_name_and_literal_arguments_test.go +98 -0
  16. package/internal/graph/diagnostics.go +23 -0
  17. package/internal/graph/dump.go +390 -0
  18. package/internal/graph/dump_evidence_skips_leading_trivia_test.go +92 -0
  19. package/internal/graph/dump_serializes_the_full_graph_test.go +146 -0
  20. package/internal/graph/edge_spans_point_to_resolved_usage_test.go +90 -0
  21. package/internal/graph/edges.go +538 -0
  22. package/internal/graph/edges_cover_class_level_references_test.go +93 -0
  23. package/internal/graph/enum_initializer_calls_are_edges_test.go +63 -0
  24. package/internal/graph/exports.go +60 -0
  25. package/internal/graph/file_diagnostics_match_tsgo_code_and_location_test.go +59 -0
  26. package/internal/graph/file_diagnostics_report_code_column_and_message_test.go +69 -0
  27. package/internal/graph/function_locals_do_not_collide_test.go +66 -0
  28. package/internal/graph/gitignore.go +51 -0
  29. package/internal/graph/graph.go +119 -0
  30. package/internal/graph/heritage_edges_cover_implements_and_interface_extends_test.go +76 -0
  31. package/internal/graph/heritage_edges_keep_extends_and_implements_to_same_base_test.go +61 -0
  32. package/internal/graph/heritage_edges_record_extends_versus_implements_origin_test.go +54 -0
  33. package/internal/graph/heritage_edges_traverse_barrels_and_stop_at_external_leaves_test.go +112 -0
  34. package/internal/graph/namespace_dotted_form_qualifies_deeply_test.go +60 -0
  35. package/internal/graph/namespace_members_become_qualified_nodes_and_edges_test.go +100 -0
  36. package/internal/graph/node_identity_is_position_invariant_test.go +84 -0
  37. package/internal/graph/node_modifiers_emit_union_strings_test.go +118 -0
  38. package/internal/graph/nodes_are_marked_exported_through_the_export_table_test.go +61 -0
  39. package/internal/graph/overload_implementation_replaces_signature_test.go +54 -0
  40. package/internal/graph/property_member_edges_remain_owner_visible_test.go +83 -0
  41. package/internal/graph/resolve.go +102 -0
  42. package/internal/graph/resolve_prefers_implementation_declaration_test.go +50 -0
  43. package/internal/graph/resolve_unwraps_barrel_reexport_to_real_declaration_test.go +230 -0
  44. package/internal/graph/type_ref_edges_cover_typeof_as_satisfies_test.go +79 -0
  45. package/internal/graph/type_ref_edges_resolve_named_types_across_files_test.go +52 -0
  46. package/internal/graph/value_access_edges_cover_properties_test.go +96 -0
  47. package/internal/graph/value_call_edges_cover_jsx_components_test.go +82 -0
  48. package/internal/graph/value_call_edges_cover_methods_and_constructors_test.go +70 -0
  49. package/internal/graph/value_call_edges_cover_variable_bound_callables_test.go +46 -0
  50. package/internal/graph/value_call_edges_do_not_double_record_invoked_member_access_test.go +65 -0
  51. package/internal/graph/value_call_edges_record_construct_versus_call_origin_test.go +75 -0
  52. package/internal/graph/value_call_edges_resolve_across_files_and_dedupe_test.go +55 -0
  53. package/internal/graph/value_call_edges_skip_self_and_unresolved_test.go +74 -0
  54. package/lib/TtscService.d.ts +55 -0
  55. package/lib/TtscService.js +93 -0
  56. package/lib/TtscService.js.map +1 -0
  57. package/lib/compiler/internal/residentTransformProcess.d.ts +47 -0
  58. package/lib/compiler/internal/residentTransformProcess.js +187 -0
  59. package/lib/compiler/internal/residentTransformProcess.js.map +1 -0
  60. package/lib/compiler/internal/startResidentTransform.d.ts +26 -0
  61. package/lib/compiler/internal/startResidentTransform.js +98 -0
  62. package/lib/compiler/internal/startResidentTransform.js.map +1 -0
  63. package/lib/index.d.ts +1 -0
  64. package/lib/index.js +1 -0
  65. package/lib/index.js.map +1 -1
  66. package/lib/plugin/internal/loadProjectPlugins.js +17 -3
  67. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  68. package/lib/structures/ITtscPlugin.d.ts +7 -5
  69. package/lib/structures/ITtscPluginFactoryContext.d.ts +24 -1
  70. package/package.json +8 -8
  71. package/shim/ast/shim.go +6 -0
  72. package/shim/checker/shim.go +22 -12
  73. package/src/TtscService.ts +94 -0
  74. package/src/compiler/internal/residentTransformProcess.ts +216 -0
  75. package/src/compiler/internal/startResidentTransform.ts +126 -0
  76. package/src/index.ts +1 -0
  77. package/src/plugin/internal/loadProjectPlugins.ts +17 -3
  78. package/src/structures/ITtscPlugin.ts +7 -5
  79. package/src/structures/ITtscPluginFactoryContext.ts +26 -1
  80. package/utility/host.go +8 -2
  81. package/utility/serve.go +158 -0
@@ -0,0 +1,70 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestValueCallEdgesCoverMethodsAndConstructors verifies that the value-call walk
11
+ // reaches what the checker resolves but the first cut dropped: a method-to-method
12
+ // call lands on the callee's method node, and a `new T()` lands on T's class node,
13
+ // both attributed to the calling method.
14
+ //
15
+ // Before method nodes existed, a callee that resolved to a method symbol was
16
+ // dropped (no node to point at) and a `new` expression was never visited, so a
17
+ // class whose logic lived in method bodies showed almost no outgoing edges. This
18
+ // pins that gap closed — the load-bearing reason a model could trust the graph
19
+ // for an architecture question instead of re-reading the source.
20
+ //
21
+ // 1. Compile a fixture where Controller.handle calls Service.run (via a parameter
22
+ // and a constructed value) and constructs a Service.
23
+ // 2. Build the graph.
24
+ // 3. Assert handle -> Service.run (value-call, deduped) and handle -> Service
25
+ // (the new-expression constructor edge) both exist.
26
+ func TestValueCallEdgesCoverMethodsAndConstructors(t *testing.T) {
27
+ root := t.TempDir()
28
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
29
+ writeFile(t, filepath.Join(root, "src", "main.ts"), `export class Service {
30
+ run(): void {}
31
+ }
32
+ export class Controller {
33
+ handle(s: Service): void {
34
+ s.run();
35
+ const made = new Service();
36
+ made.run();
37
+ }
38
+ }
39
+ `)
40
+
41
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
42
+ if err != nil {
43
+ t.Fatal(err)
44
+ }
45
+ if len(diags) != 0 {
46
+ t.Fatalf("unexpected diagnostics: %v", diags)
47
+ }
48
+ defer func() { _ = prog.Close() }()
49
+
50
+ graph := Build(prog)
51
+ path := sourceFile(t, prog, "main.ts").FileName()
52
+
53
+ handle := nodeID(path, "Controller.handle", NodeMethod)
54
+ run := nodeID(path, "Service.run", NodeMethod)
55
+ service := nodeID(path, "Service", NodeClass)
56
+
57
+ // The callee method node both endpoints need must exist as a real node.
58
+ if _, ok := graph.Nodes[run]; !ok {
59
+ t.Fatalf("Build did not record the method node Service.run; have %v", nodeIDSet(graph))
60
+ }
61
+ // Method-to-method: handle calls Service.run (from s.run() and made.run(),
62
+ // deduped to a single edge) — the call the first cut dropped.
63
+ if !hasEdge(graph, handle, run, EdgeValueCall) {
64
+ t.Fatalf("missing value-call edge Controller.handle -> Service.run; edges: %v", graph.Edges)
65
+ }
66
+ // Constructor: `new Service()` is a value-call from handle to the class node.
67
+ if !hasEdge(graph, handle, service, EdgeValueCall) {
68
+ t.Fatalf("missing value-call edge Controller.handle -> Service (new); edges: %v", graph.Edges)
69
+ }
70
+ }
@@ -0,0 +1,46 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestValueCallEdgesCoverVariableBoundCallables verifies that a call made inside a
11
+ // top-level variable-bound function (a `const fn = () => …`) is an edge from that
12
+ // variable node. The first cut never walked a variable initializer, so an
13
+ // arrow-const that called a function produced no edge — a common modern-TS shape
14
+ // the graph silently missed.
15
+ //
16
+ // 1. Compile a fixture where `const handler = () => { helper(); }`.
17
+ // 2. Build the graph.
18
+ // 3. Assert a handler -> helper value-call edge exists.
19
+ func TestValueCallEdgesCoverVariableBoundCallables(t *testing.T) {
20
+ root := t.TempDir()
21
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
22
+ writeFile(t, filepath.Join(root, "src", "main.ts"), `export function helper(): void {}
23
+ export const handler = (): void => {
24
+ helper();
25
+ };
26
+ `)
27
+
28
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
29
+ if err != nil {
30
+ t.Fatal(err)
31
+ }
32
+ if len(diags) != 0 {
33
+ t.Fatalf("unexpected diagnostics: %v", diags)
34
+ }
35
+ defer func() { _ = prog.Close() }()
36
+
37
+ graph := Build(prog)
38
+ path := sourceFile(t, prog, "main.ts").FileName()
39
+
40
+ handler := nodeID(path, "handler", NodeVariable)
41
+ helper := nodeID(path, "helper", NodeFunction)
42
+
43
+ if !hasEdge(graph, handler, helper, EdgeValueCall) {
44
+ t.Fatalf("missing value-call edge handler -> helper (variable-bound callable body); edges: %v", graph.Edges)
45
+ }
46
+ }
@@ -0,0 +1,65 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestValueCallEdgesDoNotDoubleRecordInvokedMemberAccess verifies a property
11
+ // access used as an invocation target remains only a value-call edge.
12
+ //
13
+ // Splitting value-call from value-access is useful only if method calls and
14
+ // tagged-template calls do not also look like plain property reads. Otherwise MCP
15
+ // flow ranking cannot tell invoked behavior from state/accessor evidence.
16
+ //
17
+ // 1. Compile method, constructor, and tagged-template calls whose callee/tag is
18
+ // a property access expression.
19
+ // 2. Build the graph.
20
+ // 3. Assert each target has a value-call edge and no duplicate value-access
21
+ // edge from the same caller.
22
+ func TestValueCallEdgesDoNotDoubleRecordInvokedMemberAccess(t *testing.T) {
23
+ root := t.TempDir()
24
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
25
+ writeFile(t, filepath.Join(root, "src", "main.ts"), "export namespace Providers {\n"+
26
+ " export class Service {\n"+
27
+ " run(): void {}\n"+
28
+ " }\n"+
29
+ "}\n"+
30
+ "export namespace Tags {\n"+
31
+ " export function html(strings: TemplateStringsArray): string {\n"+
32
+ " return strings[0]\n"+
33
+ " }\n"+
34
+ "}\n"+
35
+ "export function handle(service: Providers.Service): string {\n"+
36
+ " service.run()\n"+
37
+ " new Providers.Service()\n"+
38
+ " return Tags.html`ok`\n"+
39
+ "}\n")
40
+
41
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
42
+ if err != nil {
43
+ t.Fatal(err)
44
+ }
45
+ if len(diags) != 0 {
46
+ t.Fatalf("unexpected diagnostics: %v", diags)
47
+ }
48
+ defer func() { _ = prog.Close() }()
49
+
50
+ graph := Build(prog)
51
+ path := sourceFile(t, prog, "main.ts").FileName()
52
+ handle := nodeID(path, "handle", NodeFunction)
53
+ run := nodeID(path, "Providers.Service.run", NodeMethod)
54
+ service := nodeID(path, "Providers.Service", NodeClass)
55
+ html := nodeID(path, "Tags.html", NodeFunction)
56
+
57
+ for _, target := range []string{run, service, html} {
58
+ if !hasEdge(graph, handle, target, EdgeValueCall) {
59
+ t.Fatalf("missing value-call edge handle -> %s; edges: %v", target, graph.Edges)
60
+ }
61
+ if hasEdge(graph, handle, target, EdgeValueAccess) {
62
+ t.Fatalf("invoked member access was also recorded as value-access handle -> %s; edges: %v", target, graph.Edges)
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,75 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestValueCallEdgesRecordConstructVersusCallOrigin verifies that a value-call
11
+ // edge carries the syntactic Origin the dump splits into the schema's `calls`
12
+ // versus `instantiates` kinds: a plain call records "call", a `new T()` records
13
+ // "new".
14
+ //
15
+ // The internal model keeps one EdgeValueCall kind so the existing MCP stays
16
+ // untouched; the finer distinction the redesigned graph schema requires
17
+ // (`new Foo()` is an instantiation, not a call) rides on Edge.Origin. Without it
18
+ // the dump could not recover a constructor from an invocation after the fact, so
19
+ // this pins the discriminator at its source.
20
+ //
21
+ // 1. Compile a fixture where Controller.handle both calls Service.run and
22
+ // constructs `new Service()`.
23
+ // 2. Build the graph.
24
+ // 3. Assert the handle->Service.run edge has Origin "call" and the
25
+ // handle->Service edge has Origin "new".
26
+ func TestValueCallEdgesRecordConstructVersusCallOrigin(t *testing.T) {
27
+ root := t.TempDir()
28
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
29
+ writeFile(t, filepath.Join(root, "src", "main.ts"), `export class Service {
30
+ run(): void {}
31
+ }
32
+ export class Controller {
33
+ handle(s: Service): void {
34
+ s.run();
35
+ const made = new Service();
36
+ void made;
37
+ }
38
+ }
39
+ `)
40
+
41
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
42
+ if err != nil {
43
+ t.Fatal(err)
44
+ }
45
+ if len(diags) != 0 {
46
+ t.Fatalf("unexpected diagnostics: %v", diags)
47
+ }
48
+ defer func() { _ = prog.Close() }()
49
+
50
+ graph := Build(prog)
51
+ path := sourceFile(t, prog, "main.ts").FileName()
52
+
53
+ handle := nodeID(path, "Controller.handle", NodeMethod)
54
+ run := nodeID(path, "Service.run", NodeMethod)
55
+ service := nodeID(path, "Service", NodeClass)
56
+
57
+ if got := edgeOrigin(graph, handle, run, EdgeValueCall); got != "call" {
58
+ t.Fatalf("Controller.handle -> Service.run: want Origin \"call\", got %q; edges: %v", got, graph.Edges)
59
+ }
60
+ if got := edgeOrigin(graph, handle, service, EdgeValueCall); got != "new" {
61
+ t.Fatalf("Controller.handle -> Service (new): want Origin \"new\", got %q; edges: %v", got, graph.Edges)
62
+ }
63
+ }
64
+
65
+ // edgeOrigin returns the Origin of the first from->to edge of kind, or the
66
+ // sentinel "<missing>" when no such edge exists, so a test distinguishes a wrong
67
+ // origin from an absent edge.
68
+ func edgeOrigin(graph *Graph, from, to string, kind EdgeKind) string {
69
+ for _, edge := range graph.Edges {
70
+ if edge.From == from && edge.To == to && edge.Kind == kind {
71
+ return edge.Origin
72
+ }
73
+ }
74
+ return "<missing>"
75
+ }
@@ -0,0 +1,55 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestValueCallEdgesResolveAcrossFilesAndDedupe verifies that a runtime call is
11
+ // recorded as a single value-call edge to the callee's real declaration, even
12
+ // when the caller invokes it from several sites. Cross-file resolution proves the
13
+ // edge rides the checker; the dedup keeps the call graph one-edge-per-pair so an
14
+ // impact query is not skewed by how many times a function is called.
15
+ //
16
+ // 1. Compile a fixture where caller() calls helper() (declared in another file)
17
+ // twice.
18
+ // 2. Build the graph.
19
+ // 3. Assert exactly one caller -> helper value-call edge.
20
+ func TestValueCallEdgesResolveAcrossFilesAndDedupe(t *testing.T) {
21
+ root := t.TempDir()
22
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
23
+ writeFile(t, filepath.Join(root, "src", "util.ts"), `export function helper(): number {
24
+ return 1;
25
+ }
26
+ `)
27
+ writeFile(t, filepath.Join(root, "src", "main.ts"), `import { helper } from "./util";
28
+ export function caller(): number {
29
+ return helper() + helper();
30
+ }
31
+ `)
32
+
33
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
34
+ if err != nil {
35
+ t.Fatal(err)
36
+ }
37
+ if len(diags) != 0 {
38
+ t.Fatalf("unexpected diagnostics: %v", diags)
39
+ }
40
+ defer func() { _ = prog.Close() }()
41
+
42
+ graph := Build(prog)
43
+ caller := nodeID(sourceFile(t, prog, "main.ts").FileName(), "caller", NodeFunction)
44
+ helper := nodeID(sourceFile(t, prog, "util.ts").FileName(), "helper", NodeFunction)
45
+
46
+ count := 0
47
+ for _, edge := range graph.Edges {
48
+ if edge.From == caller && edge.To == helper && edge.Kind == EdgeValueCall {
49
+ count++
50
+ }
51
+ }
52
+ if count != 1 {
53
+ t.Fatalf("expected exactly one caller -> helper value-call edge, got %d; edges: %v", count, graph.Edges)
54
+ }
55
+ }
@@ -0,0 +1,74 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestValueCallEdgesSkipSelfAndUnresolved verifies that callEdge drops the two
11
+ // edges it is documented to skip while still recording an ordinary cross-call:
12
+ // a self-call (`rec` calling `rec`) yields no edge because `to == from`, and a
13
+ // call whose callee the checker cannot bind to a declaration (a method on an
14
+ // `any`-typed value) yields no edge because Resolve returns nil.
15
+ //
16
+ // The positive caller->helper edge is the load-bearing twin: without it the test
17
+ // would also pass if callEdge recorded nothing at all, so it proves the two skips
18
+ // are specific to a self-call and an unresolved callee, not a blanket failure to
19
+ // emit value-call edges.
20
+ //
21
+ // 1. Compile a fixture with `rec()` returning `rec()`, `caller()` calling a
22
+ // separate `helper()`, and `dynamic(host: any)` calling `host.run()`.
23
+ // 2. Build the graph.
24
+ // 3. Assert no rec->rec edge and no edge out of dynamic, but a caller->helper
25
+ // value-call edge.
26
+ func TestValueCallEdgesSkipSelfAndUnresolved(t *testing.T) {
27
+ root := t.TempDir()
28
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
29
+ writeFile(t, filepath.Join(root, "src", "main.ts"), `export function rec(): unknown {
30
+ return rec();
31
+ }
32
+ export function helper(): void {}
33
+ export function caller(): void {
34
+ helper();
35
+ }
36
+ export function dynamic(host: any): void {
37
+ host.run();
38
+ }
39
+ `)
40
+
41
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
42
+ if err != nil {
43
+ t.Fatal(err)
44
+ }
45
+ if len(diags) != 0 {
46
+ t.Fatalf("unexpected diagnostics: %v", diags)
47
+ }
48
+ defer func() { _ = prog.Close() }()
49
+
50
+ graph := Build(prog)
51
+ path := sourceFile(t, prog, "main.ts").FileName()
52
+
53
+ rec := nodeID(path, "rec", NodeFunction)
54
+ helper := nodeID(path, "helper", NodeFunction)
55
+ caller := nodeID(path, "caller", NodeFunction)
56
+ dynamic := nodeID(path, "dynamic", NodeFunction)
57
+
58
+ // Self-call: callEdge skips `to == from`, so rec never points at itself.
59
+ if hasEdge(graph, rec, rec, EdgeValueCall) {
60
+ t.Fatalf("a self-call recorded a rec -> rec value-call edge; edges: %v", graph.Edges)
61
+ }
62
+ // Unresolved callee: `host.run()` binds to no declaration, so dynamic gains no
63
+ // outgoing value-call edge.
64
+ for _, edge := range graph.Edges {
65
+ if edge.From == dynamic && edge.Kind == EdgeValueCall {
66
+ t.Fatalf("an unresolved callee recorded a value-call edge out of dynamic: %+v", edge)
67
+ }
68
+ }
69
+ // Positive twin: an ordinary cross-call is still recorded, proving the skips
70
+ // above are specific and not a blanket suppression.
71
+ if !hasEdge(graph, caller, helper, EdgeValueCall) {
72
+ t.Fatalf("missing value-call edge caller -> helper; edges: %v", graph.Edges)
73
+ }
74
+ }
@@ -0,0 +1,55 @@
1
+ import type { ITtscCompilerContext } from "./structures/ITtscCompilerContext";
2
+ /**
3
+ * Resident, incremental transform service for the `ttsc` TypeScript-Go
4
+ * pipeline.
5
+ *
6
+ * Where {@link TtscCompiler.transform} spawns a fresh process and recompiles the
7
+ * whole project on every call, `TtscService` keeps one long-lived host warm: it
8
+ * compiles the project once and then answers per-file transform requests from
9
+ * that warm program. A single service instance transforms many files (a watch
10
+ * server, an editor session, a codegen tool) while paying the project compile
11
+ * once instead of once per file. Sharing one host across separate worker
12
+ * processes (a Metro worker pool) is tracked in samchon/ttsc#255.
13
+ *
14
+ * The shape mirrors a legacy TypeScript `LanguageService`: construct it against
15
+ * a project context, ask it to transform individual files, and dispose it when
16
+ * done. Construction is synchronous (it launches the host); the host compiles
17
+ * in the background, so the first {@link transformFile} resolves once that
18
+ * compile lands.
19
+ *
20
+ * Resident mode runs through the linked-plugin shared host, so the project must
21
+ * declare at least one transform-stage plugin; the constructor throws
22
+ * otherwise. It does not run check-stage plugins (unlike
23
+ * {@link TtscCompiler.transform}); only the program's own type-checking gates an
24
+ * {@link updateFile}.
25
+ */
26
+ export declare class TtscService {
27
+ private readonly resident;
28
+ private readonly projectRoot;
29
+ /**
30
+ * Create a service bound to the given project context and launch its resident
31
+ * host. The context is the same shape {@link TtscCompiler} accepts; it is not
32
+ * replaceable per call.
33
+ */
34
+ constructor(context?: ITtscCompilerContext);
35
+ /**
36
+ * Return the transformed TypeScript source for one file, or `undefined` when
37
+ * the resident program does not contain it (for example a file excluded from
38
+ * the tsconfig). A relative `fileName` is resolved against the project root.
39
+ *
40
+ * Rejects when the resident host failed to compile the project; the rejection
41
+ * carries the host's diagnostics so callers can surface a real build error.
42
+ */
43
+ transformFile(fileName: string): Promise<string | undefined>;
44
+ /**
45
+ * Apply new in-memory content for one file and re-transform the project, so a
46
+ * subsequent {@link transformFile} reflects the edit without restarting the
47
+ * host. Returns whether the re-transform succeeded; `false` means the edit
48
+ * did not compile and the previous transform is still in effect. A relative
49
+ * `fileName` is resolved against the project root.
50
+ */
51
+ updateFile(fileName: string, content: string): Promise<boolean>;
52
+ /** Terminate the resident host and reject any in-flight requests. */
53
+ dispose(): void;
54
+ private absolutePath;
55
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TtscService = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const startResidentTransform_1 = require("./compiler/internal/startResidentTransform");
9
+ /**
10
+ * Resident, incremental transform service for the `ttsc` TypeScript-Go
11
+ * pipeline.
12
+ *
13
+ * Where {@link TtscCompiler.transform} spawns a fresh process and recompiles the
14
+ * whole project on every call, `TtscService` keeps one long-lived host warm: it
15
+ * compiles the project once and then answers per-file transform requests from
16
+ * that warm program. A single service instance transforms many files (a watch
17
+ * server, an editor session, a codegen tool) while paying the project compile
18
+ * once instead of once per file. Sharing one host across separate worker
19
+ * processes (a Metro worker pool) is tracked in samchon/ttsc#255.
20
+ *
21
+ * The shape mirrors a legacy TypeScript `LanguageService`: construct it against
22
+ * a project context, ask it to transform individual files, and dispose it when
23
+ * done. Construction is synchronous (it launches the host); the host compiles
24
+ * in the background, so the first {@link transformFile} resolves once that
25
+ * compile lands.
26
+ *
27
+ * Resident mode runs through the linked-plugin shared host, so the project must
28
+ * declare at least one transform-stage plugin; the constructor throws
29
+ * otherwise. It does not run check-stage plugins (unlike
30
+ * {@link TtscCompiler.transform}); only the program's own type-checking gates an
31
+ * {@link updateFile}.
32
+ */
33
+ class TtscService {
34
+ resident;
35
+ projectRoot;
36
+ /**
37
+ * Create a service bound to the given project context and launch its resident
38
+ * host. The context is the same shape {@link TtscCompiler} accepts; it is not
39
+ * replaceable per call.
40
+ */
41
+ constructor(context = {}) {
42
+ const started = (0, startResidentTransform_1.startResidentTransform)({
43
+ ...context,
44
+ env: context.env ? { ...context.env } : undefined,
45
+ plugins: Array.isArray(context.plugins)
46
+ ? [...context.plugins]
47
+ : context.plugins,
48
+ });
49
+ this.resident = started.process;
50
+ this.projectRoot = started.projectRoot;
51
+ }
52
+ /**
53
+ * Return the transformed TypeScript source for one file, or `undefined` when
54
+ * the resident program does not contain it (for example a file excluded from
55
+ * the tsconfig). A relative `fileName` is resolved against the project root.
56
+ *
57
+ * Rejects when the resident host failed to compile the project; the rejection
58
+ * carries the host's diagnostics so callers can surface a real build error.
59
+ */
60
+ async transformFile(fileName) {
61
+ const reply = await this.resident.request({
62
+ file: this.absolutePath(fileName),
63
+ });
64
+ return reply.found === true && typeof reply.typescript === "string"
65
+ ? reply.typescript
66
+ : undefined;
67
+ }
68
+ /**
69
+ * Apply new in-memory content for one file and re-transform the project, so a
70
+ * subsequent {@link transformFile} reflects the edit without restarting the
71
+ * host. Returns whether the re-transform succeeded; `false` means the edit
72
+ * did not compile and the previous transform is still in effect. A relative
73
+ * `fileName` is resolved against the project root.
74
+ */
75
+ async updateFile(fileName, content) {
76
+ const reply = await this.resident.request({
77
+ content,
78
+ update: this.absolutePath(fileName),
79
+ });
80
+ return reply.updated === true;
81
+ }
82
+ /** Terminate the resident host and reject any in-flight requests. */
83
+ dispose() {
84
+ this.resident.dispose();
85
+ }
86
+ absolutePath(fileName) {
87
+ return node_path_1.default.isAbsolute(fileName)
88
+ ? fileName
89
+ : node_path_1.default.resolve(this.projectRoot, fileName);
90
+ }
91
+ }
92
+ exports.TtscService = TtscService;
93
+ //# sourceMappingURL=TtscService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TtscService.js","sourceRoot":"","sources":["../src/TtscService.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAG7B,uFAAoF;AAGpF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;IACmB,QAAQ,CAA2B;IACnC,WAAW,CAAS;IAErC;;;;OAIG;IACH,YAAmB,OAAO,GAAyB,EAAE;QACnD,MAAM,OAAO,GAAG,IAAA,+CAAsB,EAAC;YACrC,GAAG,OAAO;YACV,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;YACjD,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;gBACrC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;gBACtB,CAAC,CAAC,OAAO,CAAC,OAAO;SACpB,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IACzC,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,aAAa,CAAC,QAAgB;QACzC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YACxC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;SAClC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;YACjE,CAAC,CAAC,KAAK,CAAC,UAAU;YAClB,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,UAAU,CAAC,QAAgB,EAAE,OAAe;QACvD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YACxC,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;SACpC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC;IAChC,CAAC;IAED,qEAAqE;IAC9D,OAAO;QACZ,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAEO,YAAY,CAAC,QAAgB;QACnC,OAAO,mBAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAC9B,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,mBAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC/C,CAAC;CACF"}
@@ -0,0 +1,47 @@
1
+ /** Options for spawning the resident transform host. */
2
+ export interface ResidentTransformProcessOptions {
3
+ args: readonly string[];
4
+ binary: string;
5
+ cwd?: string;
6
+ env?: NodeJS.ProcessEnv;
7
+ }
8
+ /**
9
+ * Async client for the long-lived `utility-host serve` process.
10
+ *
11
+ * The host transforms the whole project once at startup, caches every file's
12
+ * transformed TypeScript, then answers newline-delimited requests. This class
13
+ * speaks that protocol: each {@link request} writes one JSON line and the host
14
+ * replies with one line, matched FIFO. A transform request (`{"file":...}`) is
15
+ * answered with `{"typescript":...,"found":...}`, and an update request
16
+ * (`{"update":...,"content":...}`) with `{"updated":...}`.
17
+ *
18
+ * One resident process answers every request from one service instead of
19
+ * spawning a fresh `transform` subprocess per call, so a single process pays
20
+ * the project compile once (samchon/ttsc#255).
21
+ */
22
+ export declare class ResidentTransformProcess {
23
+ private readonly child;
24
+ private readonly reader;
25
+ private readonly pending;
26
+ private stderr;
27
+ private failure;
28
+ constructor(options: ResidentTransformProcessOptions);
29
+ /**
30
+ * Send one request to the host and resolve with its parsed JSON reply. The
31
+ * host answers in FIFO order, so the caller interprets the reply shape by the
32
+ * payload it sent (a transform reply vs an update reply). Rejects when the
33
+ * host has already failed or exited, or if writing the request fails.
34
+ */
35
+ request(payload: Record<string, unknown>): Promise<Record<string, unknown>>;
36
+ /**
37
+ * Terminate the resident process and reject any in-flight requests. Safe to
38
+ * call more than once.
39
+ */
40
+ dispose(): void;
41
+ private onLine;
42
+ private onReaderClose;
43
+ private fail;
44
+ private rejectAll;
45
+ private removePending;
46
+ private exitError;
47
+ }