ttsc 0.18.2 → 0.18.4
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/cmd/ttscgraph/main.go +18 -12
- package/cmd/ttscgraph/run_help_version_and_bad_flags_exit_codes_test.go +23 -5
- package/cmd/ttscgraph/serve.go +863 -0
- package/cmd/ttscgraph/serve_protocol_rejects_invalid_request_line_test.go +43 -0
- package/cmd/ttscgraph/serve_protocol_retries_session_creation_test.go +83 -0
- package/cmd/ttscgraph/serve_protocol_reuses_session_test.go +35 -0
- package/cmd/ttscgraph/serve_session_applies_source_edit_incrementally_test.go +55 -0
- package/cmd/ttscgraph/serve_session_fails_closed_on_deleted_config_test.go +68 -0
- package/cmd/ttscgraph/serve_session_fails_closed_on_invalid_config_test.go +50 -0
- package/cmd/ttscgraph/serve_session_keeps_missing_literal_root_unchanged_test.go +46 -0
- package/cmd/ttscgraph/serve_session_rebuilds_import_graph_change_test.go +60 -0
- package/cmd/ttscgraph/serve_session_reloads_created_literal_root_test.go +44 -0
- package/cmd/ttscgraph/serve_session_reloads_extended_config_change_test.go +45 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_conditional_export_test.go +60 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_config_types_test.go +44 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_excluded_import_test.go +62 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_package_export_test.go +46 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_package_import_test.go +46 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_path_alias_test.go +47 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_reference_path_test.go +42 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_root_dirs_target_test.go +46 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_type_reference_test.go +42 -0
- package/cmd/ttscgraph/serve_session_reloads_root_file_set_test.go +52 -0
- package/cmd/ttscgraph/serve_session_reloads_source_deleted_during_load_test.go +60 -0
- package/cmd/ttscgraph/serve_session_reloads_transitive_project_reference_test.go +55 -0
- package/cmd/ttscgraph/serve_session_reloads_when_program_plugin_linked_test.go +53 -0
- package/cmd/ttscgraph/serve_session_reuses_unchanged_preamble_source_test.go +45 -0
- package/cmd/ttscgraph/serve_session_reuses_unchanged_snapshot_test.go +52 -0
- package/cmd/ttscgraph/serve_session_revisits_source_edited_during_load_test.go +61 -0
- package/cmd/ttscgraph/serve_session_tolerates_os_invalid_resolution_candidate_test.go +56 -0
- package/cmd/ttscgraph/serve_session_tracks_all_module_specifier_forms_test.go +41 -0
- package/cmd/ttscgraph/serve_session_tracks_diamond_project_references_once_test.go +61 -0
- package/driver/emit_plugin.go +27 -12
- package/driver/plugins.go +21 -5
- package/driver/program.go +43 -14
- package/driver/session.go +11 -3
- package/lib/TtscCompiler.d.ts +3 -2
- package/lib/TtscCompiler.js +6 -14
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +14 -1
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +7 -0
- package/lib/compiler/internal/runBuild.js +176 -16
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/structures/ITtscCompilerResult.d.ts +8 -5
- package/package.json +8 -8
- package/shim/printer/shim.go +22 -4
- package/src/TtscCompiler.ts +4 -16
- package/src/compiler/internal/compileProjectInMemory.ts +17 -1
- package/src/compiler/internal/runBuild.ts +237 -23
- package/src/structures/ITtscCompilerResult.ts +8 -5
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsNewlyResolvedExcludedImport verifies a previously
|
|
10
|
+
// missing relative import becomes visible even when the new file is not a
|
|
11
|
+
// tsconfig root.
|
|
12
|
+
//
|
|
13
|
+
// Comparing only parsed root file names misses this transition: `files` stays
|
|
14
|
+
// unchanged while module resolution changes from unresolved to resolved. The
|
|
15
|
+
// session snapshots concrete resolution candidates and reloads when one appears.
|
|
16
|
+
//
|
|
17
|
+
// 1. Compile one root that imports missing, excluded `generated.ts`.
|
|
18
|
+
// 2. Create that module without changing the importer or tsconfig roots.
|
|
19
|
+
// 3. Assert reload mode and the newly-resolved declaration and call edge.
|
|
20
|
+
func TestServeSessionReloadsNewlyResolvedExcludedImport(t *testing.T) {
|
|
21
|
+
root := t.TempDir()
|
|
22
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
23
|
+
"compilerOptions": { "target": "ES2022", "module": "commonjs", "strict": true },
|
|
24
|
+
"files": ["src/index.ts"]
|
|
25
|
+
}`)
|
|
26
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "import { generated } from './generated';\nexport function main(): void { generated(); }\n")
|
|
27
|
+
|
|
28
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
29
|
+
if err != nil {
|
|
30
|
+
t.Fatal(err)
|
|
31
|
+
}
|
|
32
|
+
defer session.Close()
|
|
33
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
generated := filepath.Join(root, "src", "generated.ts")
|
|
38
|
+
if err := os.WriteFile(generated, []byte("export function generated(): void {}\n"), 0o644); err != nil {
|
|
39
|
+
t.Fatal(err)
|
|
40
|
+
}
|
|
41
|
+
dump, mode, changed, err := session.Snapshot()
|
|
42
|
+
if err != nil {
|
|
43
|
+
t.Fatal(err)
|
|
44
|
+
}
|
|
45
|
+
if dump == nil || mode != "reload" || !changed || !hasDumpNode(*dump, "generated") {
|
|
46
|
+
t.Fatalf("new resolution = dump:%v mode:%q changed:%v nodes:%#v", dump != nil, mode, changed, dump)
|
|
47
|
+
}
|
|
48
|
+
mainID, generatedID := "", ""
|
|
49
|
+
for _, node := range dump.Nodes {
|
|
50
|
+
if node.Name == "main" {
|
|
51
|
+
mainID = node.ID
|
|
52
|
+
} else if node.Name == "generated" {
|
|
53
|
+
generatedID = node.ID
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for _, edge := range dump.Edges {
|
|
57
|
+
if edge.From == mainID && edge.To == generatedID && edge.Kind == "calls" {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
t.Fatalf("reloaded graph omitted main -> generated call: %#v", dump.Edges)
|
|
62
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsNewlyResolvedPackageExport verifies an exports target
|
|
10
|
+
// appearing under an existing package triggers module-resolution reload.
|
|
11
|
+
func TestServeSessionReloadsNewlyResolvedPackageExport(t *testing.T) {
|
|
12
|
+
root := t.TempDir()
|
|
13
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
14
|
+
"compilerOptions": { "target": "ES2022", "module": "commonjs" },
|
|
15
|
+
"files": ["src/index.ts"]
|
|
16
|
+
}`)
|
|
17
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "import { feature } from 'fixture-package/feature';\nexport function main(): void { feature(); }\n")
|
|
18
|
+
writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "package.json"), `{
|
|
19
|
+
"name": "fixture-package",
|
|
20
|
+
"exports": { "./feature": "./dist/feature.js" }
|
|
21
|
+
}`)
|
|
22
|
+
|
|
23
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
24
|
+
if err != nil {
|
|
25
|
+
t.Fatal(err)
|
|
26
|
+
}
|
|
27
|
+
defer session.Close()
|
|
28
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
29
|
+
t.Fatal(err)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
target := filepath.Join(root, "node_modules", "fixture-package", "dist", "feature.ts")
|
|
33
|
+
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
if err := os.WriteFile(target, []byte("export function feature(): void {}\n"), 0o644); err != nil {
|
|
37
|
+
t.Fatal(err)
|
|
38
|
+
}
|
|
39
|
+
dump, mode, changed, err := session.Snapshot()
|
|
40
|
+
if err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
if dump == nil || mode != "reload" || !changed {
|
|
44
|
+
t.Fatalf("new package export target = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsNewlyResolvedPackageImport verifies wildcard imports
|
|
10
|
+
// targets are tracked with the wildcard text matched from the package key.
|
|
11
|
+
func TestServeSessionReloadsNewlyResolvedPackageImport(t *testing.T) {
|
|
12
|
+
root := t.TempDir()
|
|
13
|
+
writeGraphFile(t, filepath.Join(root, "package.json"), `{
|
|
14
|
+
"name": "fixture-project",
|
|
15
|
+
"imports": { "#generated/*": "./generated/*.js" }
|
|
16
|
+
}`)
|
|
17
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
18
|
+
"compilerOptions": { "target": "ES2022", "module": "nodenext", "moduleResolution": "nodenext" },
|
|
19
|
+
"files": ["src/index.ts"]
|
|
20
|
+
}`)
|
|
21
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "import { generated } from '#generated/value';\nexport function main(): void { generated(); }\n")
|
|
22
|
+
|
|
23
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
24
|
+
if err != nil {
|
|
25
|
+
t.Fatal(err)
|
|
26
|
+
}
|
|
27
|
+
defer session.Close()
|
|
28
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
29
|
+
t.Fatal(err)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
target := filepath.Join(root, "generated", "value.ts")
|
|
33
|
+
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
if err := os.WriteFile(target, []byte("export function generated(): void {}\n"), 0o644); err != nil {
|
|
37
|
+
t.Fatal(err)
|
|
38
|
+
}
|
|
39
|
+
dump, mode, changed, err := session.Snapshot()
|
|
40
|
+
if err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
if dump == nil || mode != "reload" || !changed {
|
|
44
|
+
t.Fatalf("new package imports target = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsNewlyResolvedPathAlias verifies a missing paths target
|
|
10
|
+
// is tracked even when it is excluded from the tsconfig root set.
|
|
11
|
+
func TestServeSessionReloadsNewlyResolvedPathAlias(t *testing.T) {
|
|
12
|
+
root := t.TempDir()
|
|
13
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
14
|
+
"compilerOptions": {
|
|
15
|
+
"target": "ES2022",
|
|
16
|
+
"module": "commonjs",
|
|
17
|
+
"baseUrl": ".",
|
|
18
|
+
"paths": { "@generated/*": ["generated/*"] }
|
|
19
|
+
},
|
|
20
|
+
"files": ["src/index.ts"]
|
|
21
|
+
}`)
|
|
22
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "import { generated } from '@generated/value';\nexport function main(): void { generated(); }\n")
|
|
23
|
+
|
|
24
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
25
|
+
if err != nil {
|
|
26
|
+
t.Fatal(err)
|
|
27
|
+
}
|
|
28
|
+
defer session.Close()
|
|
29
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
30
|
+
t.Fatal(err)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
target := filepath.Join(root, "generated", "value.ts")
|
|
34
|
+
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
35
|
+
t.Fatal(err)
|
|
36
|
+
}
|
|
37
|
+
if err := os.WriteFile(target, []byte("export function generated(): void {}\n"), 0o644); err != nil {
|
|
38
|
+
t.Fatal(err)
|
|
39
|
+
}
|
|
40
|
+
dump, mode, changed, err := session.Snapshot()
|
|
41
|
+
if err != nil {
|
|
42
|
+
t.Fatal(err)
|
|
43
|
+
}
|
|
44
|
+
if dump == nil || mode != "reload" || !changed || !hasDumpNode(*dump, "generated") {
|
|
45
|
+
t.Fatalf("new paths target = dump:%v mode:%q changed:%v nodes:%#v", dump != nil, mode, changed, dump)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsNewlyResolvedReferencePath verifies triple-slash path
|
|
10
|
+
// directives participate in freshness even though they are not AST statements.
|
|
11
|
+
func TestServeSessionReloadsNewlyResolvedReferencePath(t *testing.T) {
|
|
12
|
+
root := t.TempDir()
|
|
13
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
14
|
+
"compilerOptions": { "target": "ES2022", "module": "commonjs" },
|
|
15
|
+
"files": ["src/index.ts"]
|
|
16
|
+
}`)
|
|
17
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "/// <reference path=\"../generated/types.d.ts\" />\nexport const value: Generated = { id: 1 };\n")
|
|
18
|
+
|
|
19
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
20
|
+
if err != nil {
|
|
21
|
+
t.Fatal(err)
|
|
22
|
+
}
|
|
23
|
+
defer session.Close()
|
|
24
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
25
|
+
t.Fatal(err)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
target := filepath.Join(root, "generated", "types.d.ts")
|
|
29
|
+
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
30
|
+
t.Fatal(err)
|
|
31
|
+
}
|
|
32
|
+
if err := os.WriteFile(target, []byte("interface Generated { id: number }\n"), 0o644); err != nil {
|
|
33
|
+
t.Fatal(err)
|
|
34
|
+
}
|
|
35
|
+
dump, mode, changed, err := session.Snapshot()
|
|
36
|
+
if err != nil {
|
|
37
|
+
t.Fatal(err)
|
|
38
|
+
}
|
|
39
|
+
if dump == nil || mode != "reload" || !changed {
|
|
40
|
+
t.Fatalf("new reference path = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsNewlyResolvedRootDirsTarget verifies virtual relative
|
|
10
|
+
// paths across rootDirs participate in module-resolution freshness.
|
|
11
|
+
func TestServeSessionReloadsNewlyResolvedRootDirsTarget(t *testing.T) {
|
|
12
|
+
root := t.TempDir()
|
|
13
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
14
|
+
"compilerOptions": {
|
|
15
|
+
"target": "ES2022",
|
|
16
|
+
"module": "commonjs",
|
|
17
|
+
"rootDirs": ["src", "generated"]
|
|
18
|
+
},
|
|
19
|
+
"files": ["src/views/index.ts"]
|
|
20
|
+
}`)
|
|
21
|
+
writeGraphFile(t, filepath.Join(root, "src", "views", "index.ts"), "import { template } from './template';\nexport function render(): void { template(); }\n")
|
|
22
|
+
|
|
23
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
24
|
+
if err != nil {
|
|
25
|
+
t.Fatal(err)
|
|
26
|
+
}
|
|
27
|
+
defer session.Close()
|
|
28
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
29
|
+
t.Fatal(err)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
target := filepath.Join(root, "generated", "views", "template.ts")
|
|
33
|
+
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
if err := os.WriteFile(target, []byte("export function template(): void {}\n"), 0o644); err != nil {
|
|
37
|
+
t.Fatal(err)
|
|
38
|
+
}
|
|
39
|
+
dump, mode, changed, err := session.Snapshot()
|
|
40
|
+
if err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
if dump == nil || mode != "reload" || !changed || !hasDumpNode(*dump, "template") {
|
|
44
|
+
t.Fatalf("new rootDirs target = dump:%v mode:%q changed:%v nodes:%#v", dump != nil, mode, changed, dump)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsNewlyResolvedTypeReference verifies triple-slash type
|
|
10
|
+
// directives honor configured typeRoots when a missing package appears.
|
|
11
|
+
func TestServeSessionReloadsNewlyResolvedTypeReference(t *testing.T) {
|
|
12
|
+
root := t.TempDir()
|
|
13
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
14
|
+
"compilerOptions": { "target": "ES2022", "typeRoots": ["./types"] },
|
|
15
|
+
"files": ["src/index.ts"]
|
|
16
|
+
}`)
|
|
17
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "/// <reference types=\"fixture-types\" />\nexport const value: FixtureType = { id: 1 };\n")
|
|
18
|
+
|
|
19
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
20
|
+
if err != nil {
|
|
21
|
+
t.Fatal(err)
|
|
22
|
+
}
|
|
23
|
+
defer session.Close()
|
|
24
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
25
|
+
t.Fatal(err)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
target := filepath.Join(root, "types", "fixture-types", "index.d.ts")
|
|
29
|
+
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
30
|
+
t.Fatal(err)
|
|
31
|
+
}
|
|
32
|
+
if err := os.WriteFile(target, []byte("interface FixtureType { id: number }\n"), 0o644); err != nil {
|
|
33
|
+
t.Fatal(err)
|
|
34
|
+
}
|
|
35
|
+
dump, mode, changed, err := session.Snapshot()
|
|
36
|
+
if err != nil {
|
|
37
|
+
t.Fatal(err)
|
|
38
|
+
}
|
|
39
|
+
if dump == nil || mode != "reload" || !changed {
|
|
40
|
+
t.Fatalf("new type reference = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsRootFileSet verifies include-glob additions and
|
|
10
|
+
// deletions replace the compiler session rather than leaving stale roots.
|
|
11
|
+
//
|
|
12
|
+
// A one-file UpdateProgram cannot add or remove config roots. The session must
|
|
13
|
+
// re-evaluate the tsconfig file set before source hashing and reload whenever
|
|
14
|
+
// that set changes.
|
|
15
|
+
//
|
|
16
|
+
// 1. Add `AddedRoot` under an included directory and assert reload plus presence.
|
|
17
|
+
// 2. Delete the original `BeforeEdit` root.
|
|
18
|
+
// 3. Assert another reload removes the deleted declaration.
|
|
19
|
+
func TestServeSessionReloadsRootFileSet(t *testing.T) {
|
|
20
|
+
root := graphSessionFixture(t)
|
|
21
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
22
|
+
if err != nil {
|
|
23
|
+
t.Fatal(err)
|
|
24
|
+
}
|
|
25
|
+
defer session.Close()
|
|
26
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
27
|
+
t.Fatal(err)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
added := filepath.Join(root, "src", "added.ts")
|
|
31
|
+
if err := os.WriteFile(added, []byte("export class AddedRoot {}\n"), 0o644); err != nil {
|
|
32
|
+
t.Fatal(err)
|
|
33
|
+
}
|
|
34
|
+
dump, mode, changed, err := session.Snapshot()
|
|
35
|
+
if err != nil {
|
|
36
|
+
t.Fatal(err)
|
|
37
|
+
}
|
|
38
|
+
if dump == nil || mode != "reload" || !changed || !hasDumpNode(*dump, "AddedRoot") {
|
|
39
|
+
t.Fatalf("added root was not reloaded: dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if err := os.Remove(filepath.Join(root, "src", "index.ts")); err != nil {
|
|
43
|
+
t.Fatal(err)
|
|
44
|
+
}
|
|
45
|
+
dump, mode, changed, err = session.Snapshot()
|
|
46
|
+
if err != nil {
|
|
47
|
+
t.Fatal(err)
|
|
48
|
+
}
|
|
49
|
+
if dump == nil || mode != "reload" || !changed || hasDumpNode(*dump, "BeforeEdit") {
|
|
50
|
+
t.Fatalf("deleted root remained in graph: dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
|
|
8
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestServeSessionReloadsSourceDeletedDuringLoad verifies a program source
|
|
12
|
+
// that vanishes between compiler load and state capture is still detected.
|
|
13
|
+
//
|
|
14
|
+
// captureState hashes only sources it can read back from disk, so silently
|
|
15
|
+
// skipping a just-deleted file would drop it from every later freshness check:
|
|
16
|
+
// no root change, no source hash, no resolution candidate. The session must
|
|
17
|
+
// record the resident text instead so the next snapshot observes the deletion
|
|
18
|
+
// and reloads rather than serving the vanished declarations forever.
|
|
19
|
+
//
|
|
20
|
+
// 1. Load a session whose root imports `helper.ts`.
|
|
21
|
+
// 2. Delete `helper.ts` before capturing the freshness state.
|
|
22
|
+
// 3. Assert the next snapshot reloads and drops the deleted declaration.
|
|
23
|
+
func TestServeSessionReloadsSourceDeletedDuringLoad(t *testing.T) {
|
|
24
|
+
root := t.TempDir()
|
|
25
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
26
|
+
"compilerOptions": { "target": "ES2022", "module": "commonjs" },
|
|
27
|
+
"files": ["src/index.ts"]
|
|
28
|
+
}`)
|
|
29
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "import { helper } from './helper';\nexport function main(): void { helper(); }\n")
|
|
30
|
+
writeGraphFile(t, filepath.Join(root, "src", "helper.ts"), "export function helper(): void {}\n")
|
|
31
|
+
|
|
32
|
+
compiler, diags, err := driver.NewSession(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
33
|
+
if err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
if compiler == nil {
|
|
37
|
+
t.Fatalf("NewSession returned nil session (diagnostics: %v)", diags)
|
|
38
|
+
}
|
|
39
|
+
if err := os.Remove(filepath.Join(root, "src", "helper.ts")); err != nil {
|
|
40
|
+
t.Fatal(err)
|
|
41
|
+
}
|
|
42
|
+
session := &graphSession{
|
|
43
|
+
cwd: root,
|
|
44
|
+
tsconfig: "tsconfig.json",
|
|
45
|
+
compiler: compiler,
|
|
46
|
+
initialized: true,
|
|
47
|
+
}
|
|
48
|
+
defer session.Close()
|
|
49
|
+
if err := session.captureState(); err != nil {
|
|
50
|
+
t.Fatal(err)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dump, mode, changed, err := session.Snapshot()
|
|
54
|
+
if err != nil {
|
|
55
|
+
t.Fatal(err)
|
|
56
|
+
}
|
|
57
|
+
if dump == nil || mode != "reload" || !changed || hasDumpNode(*dump, "helper") {
|
|
58
|
+
t.Fatalf("deleted-during-load source survived: dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestServeSessionReloadsTransitiveProjectReference verifies config and root
|
|
10
|
+
// freshness traverses the complete project-reference graph, not only the
|
|
11
|
+
// root config's direct references.
|
|
12
|
+
func TestServeSessionReloadsTransitiveProjectReference(t *testing.T) {
|
|
13
|
+
root := t.TempDir()
|
|
14
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
15
|
+
"files": ["src/index.ts"],
|
|
16
|
+
"references": [{ "path": "./middle" }]
|
|
17
|
+
}`)
|
|
18
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "export const root = 1;\n")
|
|
19
|
+
writeGraphFile(t, filepath.Join(root, "middle", "tsconfig.json"), `{
|
|
20
|
+
"compilerOptions": { "composite": true },
|
|
21
|
+
"files": ["src/index.ts"],
|
|
22
|
+
"references": [{ "path": "../leaf" }]
|
|
23
|
+
}`)
|
|
24
|
+
writeGraphFile(t, filepath.Join(root, "middle", "src", "index.ts"), "export const middle = 1;\n")
|
|
25
|
+
writeGraphFile(t, filepath.Join(root, "leaf", "tsconfig.json"), `{
|
|
26
|
+
"compilerOptions": { "composite": true },
|
|
27
|
+
"include": ["src"]
|
|
28
|
+
}`)
|
|
29
|
+
writeGraphFile(t, filepath.Join(root, "leaf", "src", "index.ts"), "export const leaf = 1;\n")
|
|
30
|
+
|
|
31
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
32
|
+
if err != nil {
|
|
33
|
+
t.Fatal(err)
|
|
34
|
+
}
|
|
35
|
+
defer session.Close()
|
|
36
|
+
leafConfig := filepath.ToSlash(filepath.Join(root, "leaf", "tsconfig.json"))
|
|
37
|
+
if _, ok := session.configHashes[leafConfig]; !ok {
|
|
38
|
+
t.Fatalf("transitive project config was not tracked: %s; got %#v", leafConfig, session.configHashes)
|
|
39
|
+
}
|
|
40
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
added := filepath.Join(root, "leaf", "src", "added.ts")
|
|
45
|
+
if err := os.WriteFile(added, []byte("export const added = 1;\n"), 0o644); err != nil {
|
|
46
|
+
t.Fatal(err)
|
|
47
|
+
}
|
|
48
|
+
dump, mode, changed, err := session.Snapshot()
|
|
49
|
+
if err != nil {
|
|
50
|
+
t.Fatal(err)
|
|
51
|
+
}
|
|
52
|
+
if dump == nil || mode != "reload" || !changed {
|
|
53
|
+
t.Fatalf("transitive root addition = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
|
|
8
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
type serveProgramPluginProbe struct{}
|
|
12
|
+
|
|
13
|
+
func (serveProgramPluginProbe) ApplyProgram(*driver.Program, driver.PluginContext) error {
|
|
14
|
+
return nil
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// TestServeSessionReloadsWhenProgramPluginLinked verifies a project with an
|
|
18
|
+
// active linked ProgramPlugin never takes the incremental source path.
|
|
19
|
+
//
|
|
20
|
+
// ProgramPlugin hooks mutate parsed ASTs in place and run exactly once per
|
|
21
|
+
// Program, so an incremental source replacement would leave the new AST
|
|
22
|
+
// without the plugin's mutations while the rest of the graph still carries
|
|
23
|
+
// them. A content-only edit must therefore rebuild through a full reload.
|
|
24
|
+
//
|
|
25
|
+
// 1. Register a linked ProgramPlugin and open a graph session.
|
|
26
|
+
// 2. Apply a content-only edit that would otherwise refresh incrementally.
|
|
27
|
+
// 3. Assert the snapshot reports a full reload with the post-edit node.
|
|
28
|
+
func TestServeSessionReloadsWhenProgramPluginLinked(t *testing.T) {
|
|
29
|
+
t.Setenv(driver.LinkedPluginsEnv, `[{"name":"probe","stage":"transform"}]`)
|
|
30
|
+
driver.RegisterPlugin(serveProgramPluginProbe{})
|
|
31
|
+
|
|
32
|
+
root := graphSessionFixture(t)
|
|
33
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
34
|
+
if err != nil {
|
|
35
|
+
t.Fatal(err)
|
|
36
|
+
}
|
|
37
|
+
defer session.Close()
|
|
38
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
39
|
+
t.Fatal(err)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
file := filepath.Join(root, "src", "index.ts")
|
|
43
|
+
if err := os.WriteFile(file, []byte("export class AfterEdit {}\n"), 0o644); err != nil {
|
|
44
|
+
t.Fatal(err)
|
|
45
|
+
}
|
|
46
|
+
dump, mode, changed, err := session.Snapshot()
|
|
47
|
+
if err != nil {
|
|
48
|
+
t.Fatal(err)
|
|
49
|
+
}
|
|
50
|
+
if dump == nil || mode != "reload" || !changed || !hasDumpNode(*dump, "AfterEdit") {
|
|
51
|
+
t.Fatalf("program-plugin edit = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestServeSessionReusesUnchangedPreambleSource verifies injected compiler text
|
|
11
|
+
// is compared against the corresponding raw disk source, not mistaken for an
|
|
12
|
+
// edit on every snapshot.
|
|
13
|
+
func TestServeSessionReusesUnchangedPreambleSource(t *testing.T) {
|
|
14
|
+
root := t.TempDir()
|
|
15
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{"compilerOptions":{"strict":true},"files":["index.ts"]}`)
|
|
16
|
+
writeGraphFile(t, filepath.Join(root, "index.ts"), "export const value = 1;\n")
|
|
17
|
+
|
|
18
|
+
compiler, diags, err := driver.NewSession(root, "tsconfig.json", driver.LoadProgramOptions{
|
|
19
|
+
SourcePreamble: "declare const injected: number;\n",
|
|
20
|
+
})
|
|
21
|
+
if err != nil {
|
|
22
|
+
t.Fatal(err)
|
|
23
|
+
}
|
|
24
|
+
if compiler == nil {
|
|
25
|
+
t.Fatalf("NewSession returned nil session (diagnostics: %v)", diags)
|
|
26
|
+
}
|
|
27
|
+
session := &graphSession{
|
|
28
|
+
cwd: root,
|
|
29
|
+
tsconfig: "tsconfig.json",
|
|
30
|
+
compiler: compiler,
|
|
31
|
+
initialized: true,
|
|
32
|
+
}
|
|
33
|
+
defer session.Close()
|
|
34
|
+
if err := session.captureState(); err != nil {
|
|
35
|
+
t.Fatal(err)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
dump, mode, changed, err := session.Snapshot()
|
|
39
|
+
if err != nil {
|
|
40
|
+
t.Fatal(err)
|
|
41
|
+
}
|
|
42
|
+
if dump != nil || mode != "unchanged" || changed {
|
|
43
|
+
t.Fatalf("unchanged preamble source = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestServeSessionReusesUnchangedSnapshot verifies repeated graph requests do
|
|
9
|
+
// no graph rebuild when every project input is byte-identical.
|
|
10
|
+
//
|
|
11
|
+
// This is the hot MCP path. The native session still checks its config, root
|
|
12
|
+
// file set, and resident source hashes, but it must omit the dump so the Node
|
|
13
|
+
// server can retain its indexed maps without parsing or synthesizing them again.
|
|
14
|
+
//
|
|
15
|
+
// 1. Open a one-file graph session and request its initial dump.
|
|
16
|
+
// 2. Request another snapshot without touching the project.
|
|
17
|
+
// 3. Assert the second response is unchanged and carries no replacement dump.
|
|
18
|
+
func TestServeSessionReusesUnchangedSnapshot(t *testing.T) {
|
|
19
|
+
root := graphSessionFixture(t)
|
|
20
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
21
|
+
if err != nil {
|
|
22
|
+
t.Fatal(err)
|
|
23
|
+
}
|
|
24
|
+
defer session.Close()
|
|
25
|
+
|
|
26
|
+
first, mode, changed, err := session.Snapshot()
|
|
27
|
+
if err != nil {
|
|
28
|
+
t.Fatal(err)
|
|
29
|
+
}
|
|
30
|
+
if first == nil || mode != "initial" || !changed {
|
|
31
|
+
t.Fatalf("initial snapshot = dump:%v mode:%q changed:%v", first != nil, mode, changed)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
second, mode, changed, err := session.Snapshot()
|
|
35
|
+
if err != nil {
|
|
36
|
+
t.Fatal(err)
|
|
37
|
+
}
|
|
38
|
+
if second != nil || mode != "unchanged" || changed {
|
|
39
|
+
t.Fatalf("unchanged snapshot = dump:%v mode:%q changed:%v", second != nil, mode, changed)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func graphSessionFixture(t *testing.T) string {
|
|
44
|
+
t.Helper()
|
|
45
|
+
root := t.TempDir()
|
|
46
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
47
|
+
"compilerOptions": { "target": "ES2022", "module": "commonjs", "strict": true },
|
|
48
|
+
"include": ["src"]
|
|
49
|
+
}`)
|
|
50
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "export class BeforeEdit {}\n")
|
|
51
|
+
return root
|
|
52
|
+
}
|