ttsc 0.20.1 → 0.22.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/cmd/ttscserver/main.go +79 -3
- package/cmd/ttscserver/run_lsp_uses_native_plugin_manifest_source_test.go +96 -16
- package/driver/lsp.go +4 -0
- package/internal/lspserver/lsp_document_diagnostics_omits_cached_project_when_current_result_has_none_test.go +48 -0
- package/internal/lspserver/lsp_executable_project_input_change_requests_launcher_restart_test.go +98 -0
- package/internal/lspserver/lsp_external_watched_change_retains_program_test.go +53 -0
- package/internal/lspserver/lsp_hints_refresh_scheduled_by_editor_events_test.go +9 -0
- package/internal/lspserver/lsp_native_plugin_source.go +171 -55
- package/internal/lspserver/lsp_native_resident.go +110 -9
- package/internal/lspserver/lsp_plugin.go +6 -0
- package/internal/lspserver/lsp_project_context_transport_identity_test.go +43 -0
- package/internal/lspserver/lsp_project_diagnostics.go +235 -0
- package/internal/lspserver/lsp_project_diagnostics_capability_is_independent_test.go +67 -0
- package/internal/lspserver/lsp_project_diagnostics_program_input_overlap_refreshes_all_producers_test.go +78 -0
- package/internal/lspserver/lsp_project_diagnostics_refresh_accumulates_owner_freshness_test.go +65 -0
- package/internal/lspserver/lsp_project_diagnostics_refresh_keeps_each_producer_last_good_test.go +90 -0
- package/internal/lspserver/lsp_project_diagnostics_refreshes_only_input_owners_test.go +62 -0
- package/internal/lspserver/lsp_project_diagnostics_resident_unsupported_falls_back_test.go +123 -0
- package/internal/lspserver/lsp_project_diagnostics_resume_preserves_generation_test.go +52 -0
- package/internal/lspserver/lsp_project_diagnostics_translates_relative_client_project_test.go +90 -0
- package/internal/lspserver/lsp_project_input_ownership_resolves_declared_spellings_test.go +95 -0
- package/internal/lspserver/lsp_project_input_registration.go +504 -0
- package/internal/lspserver/lsp_project_input_registration_rechecks_selection_baseline_test.go +71 -0
- package/internal/lspserver/lsp_project_input_registration_reconciles_rejected_pending_request_test.go +268 -0
- package/internal/lspserver/lsp_project_input_registration_shapes_patterns_test.go +94 -0
- package/internal/lspserver/lsp_project_input_watchers_anchor_missing_paths_test.go +48 -0
- package/internal/lspserver/lsp_project_inputs.go +944 -0
- package/internal/lspserver/lsp_project_inputs_match_missing_exact_and_glob_test.go +129 -0
- package/internal/lspserver/lsp_project_inputs_reject_invalid_and_foreign_snapshots_test.go +110 -0
- package/internal/lspserver/lsp_proxy.go +591 -28
- package/internal/lspserver/lsp_reload_directories_compare_immediate_topology_test.go +188 -0
- package/internal/lspserver/lsp_reload_file_fingerprint_preserves_raw_symlink_target_test.go +47 -0
- package/internal/lspserver/lsp_resident_falls_back_when_serve_unsupported_test.go +1 -1
- package/internal/lspserver/lsp_resident_invalidation_broadcasts_program_input_overlap_test.go +60 -0
- package/internal/lspserver/lsp_resident_invalidation_targets_project_input_owners_test.go +51 -0
- package/internal/lspserver/lsp_same_transport_entries_are_deduplicated_test.go +66 -0
- package/lib/compiler/internal/project/readProjectConfig.d.ts +5 -3
- package/lib/compiler/internal/project/readProjectConfig.js +46 -14
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/residentCheckProcess.d.ts +46 -0
- package/lib/compiler/internal/residentCheckProcess.js +227 -0
- package/lib/compiler/internal/residentCheckProcess.js.map +1 -0
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +2 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +35 -18
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +64 -1
- package/lib/compiler/internal/runBuild.js +484 -7
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +1 -0
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/flags/parser.js +13 -17
- package/lib/flags/parser.js.map +1 -1
- package/lib/flags/schema.d.ts +10 -3
- package/lib/flags/schema.js +99 -0
- package/lib/flags/schema.js.map +1 -1
- package/lib/internal/projectInputPathIdentity.d.ts +23 -0
- package/lib/internal/projectInputPathIdentity.js +222 -0
- package/lib/internal/projectInputPathIdentity.js.map +1 -0
- package/lib/launcher/internal/runTtsc.d.ts +17 -0
- package/lib/launcher/internal/runTtsc.js +121 -88
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.d.ts +13 -1
- package/lib/launcher/internal/runTtscserver.js +415 -45
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/singleFileOutput.d.ts +16 -0
- package/lib/launcher/internal/singleFileOutput.js +103 -0
- package/lib/launcher/internal/singleFileOutput.js.map +1 -0
- package/lib/launcher/internal/watchTopology.d.ts +214 -2
- package/lib/launcher/internal/watchTopology.js +1560 -41
- package/lib/launcher/internal/watchTopology.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +1 -0
- package/lib/plugin/internal/loadProjectPlugins.js +5 -0
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscPluginCapabilities.d.ts +31 -0
- package/lib/structures/internal/ITtscProjectInputSnapshot.d.ts +29 -0
- package/lib/structures/internal/ITtscProjectInputSnapshot.js +3 -0
- package/lib/structures/internal/ITtscProjectInputSnapshot.js.map +1 -0
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +5 -0
- package/package.json +8 -8
- package/src/compiler/internal/project/readProjectConfig.ts +62 -8
- package/src/compiler/internal/residentCheckProcess.ts +279 -0
- package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -21
- package/src/compiler/internal/runBuild.ts +728 -12
- package/src/compiler/internal/runSingleFileEmit.ts +1 -0
- package/src/flags/parser.ts +17 -17
- package/src/flags/schema.ts +111 -3
- package/src/internal/projectInputPathIdentity.ts +273 -0
- package/src/launcher/internal/runTtsc.ts +141 -117
- package/src/launcher/internal/runTtscserver.ts +561 -54
- package/src/launcher/internal/singleFileOutput.ts +133 -0
- package/src/launcher/internal/watchTopology.ts +2008 -59
- package/src/plugin/internal/loadProjectPlugins.ts +8 -0
- package/src/structures/ITtscPluginCapabilities.ts +34 -0
- package/src/structures/internal/ITtscProjectInputSnapshot.ts +29 -0
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +5 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
package lspserver
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"net/url"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"strings"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestProjectInputsMatchMissingExactAndGlob verifies URI matching consumes the
|
|
11
|
+
// declaration snapshot rather than the set of files that happened to exist.
|
|
12
|
+
//
|
|
13
|
+
// Exact missing paths and zero-match glob populations must match future
|
|
14
|
+
// create/change/delete notifications. Unrelated files and remote URIs remain
|
|
15
|
+
// outside the filesystem contract.
|
|
16
|
+
//
|
|
17
|
+
// 1. Normalize one missing exact path and two zero-match globs as a producer
|
|
18
|
+
// publishes them, plus executable reload paths, then install that snapshot
|
|
19
|
+
// in a source.
|
|
20
|
+
// 2. Match URI spellings for future files without creating them.
|
|
21
|
+
// 3. Match only exact files, directory identity, or immediate topology
|
|
22
|
+
// entries as executable-selection input scope.
|
|
23
|
+
// 4. Reject an unrelated Markdown file and an HTTPS resource.
|
|
24
|
+
func TestProjectInputsMatchMissingExactAndGlob(t *testing.T) {
|
|
25
|
+
root := t.TempDir()
|
|
26
|
+
publishedPath := func(location string) string {
|
|
27
|
+
return filepath.ToSlash(realProjectInputPath(location))
|
|
28
|
+
}
|
|
29
|
+
snapshot, err := normalizeLSPProjectInputSnapshot(
|
|
30
|
+
LSPProjectInputSnapshot{
|
|
31
|
+
Root: publishedPath(root),
|
|
32
|
+
Files: []string{
|
|
33
|
+
publishedPath(filepath.Join(root, "docs", "missing.md")),
|
|
34
|
+
},
|
|
35
|
+
Globs: []string{
|
|
36
|
+
publishedPath(filepath.Join(root, "api", "**", "*.json")),
|
|
37
|
+
publishedPath(filepath.Join(
|
|
38
|
+
root,
|
|
39
|
+
"api",
|
|
40
|
+
"**",
|
|
41
|
+
"v[12]",
|
|
42
|
+
"{openapi,swagger}.yaml",
|
|
43
|
+
)),
|
|
44
|
+
},
|
|
45
|
+
ReloadDirectories: []string{
|
|
46
|
+
publishedPath(filepath.Join(root, "config-deps")),
|
|
47
|
+
},
|
|
48
|
+
ReloadFiles: []string{
|
|
49
|
+
publishedPath(filepath.Join(root, "lint.config.ts")),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
root,
|
|
53
|
+
)
|
|
54
|
+
if err != nil {
|
|
55
|
+
t.Fatalf("normalize project input snapshot: %v", err)
|
|
56
|
+
}
|
|
57
|
+
source := &NativePluginSource{
|
|
58
|
+
projectInputs: snapshot,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
cases := []struct {
|
|
62
|
+
location string
|
|
63
|
+
want bool
|
|
64
|
+
}{
|
|
65
|
+
{filepath.Join(root, "docs", "missing.md"), true},
|
|
66
|
+
{filepath.Join(root, "api", "openapi.json"), true},
|
|
67
|
+
{filepath.Join(root, "api", "nested", "swagger.json"), true},
|
|
68
|
+
{
|
|
69
|
+
filepath.Join(
|
|
70
|
+
root,
|
|
71
|
+
"api",
|
|
72
|
+
"nested",
|
|
73
|
+
"v[12]",
|
|
74
|
+
"{openapi,swagger}.yaml",
|
|
75
|
+
),
|
|
76
|
+
true,
|
|
77
|
+
},
|
|
78
|
+
{filepath.Join(root, "api", "nested", "v1", "openapi.yaml"), false},
|
|
79
|
+
{filepath.Join(root, "README.md"), false},
|
|
80
|
+
}
|
|
81
|
+
for _, tc := range cases {
|
|
82
|
+
uriPath := filepath.ToSlash(tc.location)
|
|
83
|
+
if filepath.VolumeName(tc.location) != "" &&
|
|
84
|
+
!strings.HasPrefix(uriPath, "/") {
|
|
85
|
+
uriPath = "/" + uriPath
|
|
86
|
+
}
|
|
87
|
+
uri := (&url.URL{
|
|
88
|
+
Scheme: "file",
|
|
89
|
+
Path: uriPath,
|
|
90
|
+
}).String()
|
|
91
|
+
if got := source.ProjectInputMatchesURI(uri); got != tc.want {
|
|
92
|
+
t.Fatalf("ProjectInputMatchesURI(%q) = %v, want %v", uri, got, tc.want)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if source.ProjectInputMatchesURI("https://example.com/openapi.json") {
|
|
96
|
+
t.Fatal("remote URL matched the local filesystem dependency contract")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
reloadCases := []struct {
|
|
100
|
+
location string
|
|
101
|
+
want bool
|
|
102
|
+
}{
|
|
103
|
+
{filepath.Join(root, "lint.config.ts"), true},
|
|
104
|
+
{filepath.Join(root, "config-deps"), true},
|
|
105
|
+
{filepath.Join(root, "config-deps", "package.json"), true},
|
|
106
|
+
{filepath.Join(root, "config-deps", "nested", "package.json"), false},
|
|
107
|
+
{filepath.Join(root, "config-deps-other", "package.json"), false},
|
|
108
|
+
{filepath.Join(root, "README.md"), false},
|
|
109
|
+
}
|
|
110
|
+
for _, tc := range reloadCases {
|
|
111
|
+
uriPath := filepath.ToSlash(tc.location)
|
|
112
|
+
if filepath.VolumeName(tc.location) != "" &&
|
|
113
|
+
!strings.HasPrefix(uriPath, "/") {
|
|
114
|
+
uriPath = "/" + uriPath
|
|
115
|
+
}
|
|
116
|
+
uri := (&url.URL{Scheme: "file", Path: uriPath}).String()
|
|
117
|
+
if got := source.ProjectInputReloadMatchesURI(uri); got != tc.want {
|
|
118
|
+
t.Fatalf(
|
|
119
|
+
"ProjectInputReloadMatchesURI(%q) = %v, want %v",
|
|
120
|
+
uri,
|
|
121
|
+
got,
|
|
122
|
+
tc.want,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if source.ProjectInputReloadMatchesURI("https://example.com/package.json") {
|
|
127
|
+
t.Fatal("remote URL matched the executable reload contract")
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
package lspserver
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestLSPProjectInputsRejectInvalidAndForeignSnapshots verifies the editor
|
|
9
|
+
// consumer enforces the same absolute-local, selected-root contract as the CLI
|
|
10
|
+
// watch consumer.
|
|
11
|
+
//
|
|
12
|
+
// The LSP manifest is an independent consumer of the sidecar protocol. It must
|
|
13
|
+
// reject malformed paths before a broad editor watcher can be redirected to an
|
|
14
|
+
// unrelated root or a Windows device namespace.
|
|
15
|
+
//
|
|
16
|
+
// 1. Normalize one valid snapshot under the selected physical root.
|
|
17
|
+
// 2. Reject relative paths, remote URLs, and a different project root.
|
|
18
|
+
// 3. Reject Windows device namespaces and malformed UNC volumes while
|
|
19
|
+
// retaining fixed drive and UNC paths.
|
|
20
|
+
// 4. Reject incomplete and malformed launcher-owned reload fingerprints.
|
|
21
|
+
func TestLSPProjectInputsRejectInvalidAndForeignSnapshots(t *testing.T) {
|
|
22
|
+
root := t.TempDir()
|
|
23
|
+
valid := LSPProjectInputSnapshot{
|
|
24
|
+
Root: root,
|
|
25
|
+
Files: []string{filepath.Join(root, "docs", "spec.md")},
|
|
26
|
+
Globs: []string{filepath.Join(root, "api", "**", "*.json")},
|
|
27
|
+
ReloadFiles: []string{filepath.Join(root, "lint.config.ts")},
|
|
28
|
+
ReloadDirectories: []string{filepath.Join(root, "config-deps")},
|
|
29
|
+
}
|
|
30
|
+
normalized, err := normalizeLSPProjectInputSnapshot(valid, root)
|
|
31
|
+
if err != nil {
|
|
32
|
+
t.Fatalf("valid snapshot: %v", err)
|
|
33
|
+
}
|
|
34
|
+
if len(normalized.Files) != 1 ||
|
|
35
|
+
len(normalized.Globs) != 1 ||
|
|
36
|
+
len(normalized.ReloadFiles) != 1 ||
|
|
37
|
+
len(normalized.ReloadDirectories) != 1 {
|
|
38
|
+
t.Fatalf("normalized snapshot = %#v", normalized)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
cases := []LSPProjectInputSnapshot{
|
|
42
|
+
{Root: "relative", Files: []string{}, Globs: []string{}},
|
|
43
|
+
{Root: root, Files: []string{"docs/spec.md"}, Globs: []string{}},
|
|
44
|
+
{Root: root, Files: []string{}, Globs: []string{"https://example.com/openapi.json"}},
|
|
45
|
+
{Root: root, ReloadFiles: []string{"lint.config.ts"}},
|
|
46
|
+
{Root: root, ReloadDirectories: []string{"config-deps"}},
|
|
47
|
+
{Root: filepath.Join(root, "foreign"), Files: []string{}, Globs: []string{}},
|
|
48
|
+
}
|
|
49
|
+
for _, snapshot := range cases {
|
|
50
|
+
if _, err := normalizeLSPProjectInputSnapshot(snapshot, root); err == nil {
|
|
51
|
+
t.Fatalf("invalid snapshot was accepted: %#v", snapshot)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
malformedFingerprintCases := []LSPProjectInputSnapshot{
|
|
56
|
+
{
|
|
57
|
+
Root: root,
|
|
58
|
+
ReloadDirectories: valid.ReloadDirectories,
|
|
59
|
+
ReloadDirectoryDigests: map[string]string{},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
Root: root,
|
|
63
|
+
ReloadDirectories: valid.ReloadDirectories,
|
|
64
|
+
ReloadDirectoryDigests: map[string]string{
|
|
65
|
+
valid.ReloadDirectories[0]: "not-a-sha256-digest",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
Root: root,
|
|
70
|
+
ReloadFiles: valid.ReloadFiles,
|
|
71
|
+
ReloadFileDigests: map[string]string{},
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
for _, snapshot := range malformedFingerprintCases {
|
|
75
|
+
if _, err := normalizeLSPProjectInputSnapshot(snapshot, root); err == nil {
|
|
76
|
+
t.Fatalf("malformed selection fingerprint was accepted: %#v", snapshot)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
windowsCases := []struct {
|
|
81
|
+
location string
|
|
82
|
+
want bool
|
|
83
|
+
}{
|
|
84
|
+
{location: `C:\project\docs\spec.md`, want: true},
|
|
85
|
+
{location: `\\server\share\docs\spec.md`, want: true},
|
|
86
|
+
{location: `\\?\C:\project\docs\spec.md`, want: true},
|
|
87
|
+
{location: `\\?\UNC\server\share\docs\spec.md`, want: true},
|
|
88
|
+
{location: `\root-only\docs\spec.md`, want: false},
|
|
89
|
+
{location: "/root-only/docs/spec.md", want: false},
|
|
90
|
+
{location: "\\\\server\\*\\docs\\spec.md", want: false},
|
|
91
|
+
{location: "\\\\server\\..\\docs\\spec.md", want: false},
|
|
92
|
+
{location: "\\\\?\\UNC\\server\\?\\docs\\spec.md", want: false},
|
|
93
|
+
{location: "C:\\project\\docs\\spec.md\x00ignored", want: false},
|
|
94
|
+
{location: `\\?\GLOBALROOT\Device\HarddiskVolume1`, want: false},
|
|
95
|
+
{location: `\\.\pipe\ttsc`, want: false},
|
|
96
|
+
}
|
|
97
|
+
for _, tc := range windowsCases {
|
|
98
|
+
if got := isAbsoluteLocalLSPProjectInputPath(tc.location, "windows"); got != tc.want {
|
|
99
|
+
t.Fatalf(
|
|
100
|
+
"isAbsoluteLocalLSPProjectInputPath(%q, windows) = %v, want %v",
|
|
101
|
+
tc.location,
|
|
102
|
+
got,
|
|
103
|
+
tc.want,
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if !isAbsoluteLocalLSPProjectInputPath("/project/docs/spec.md", "linux") {
|
|
108
|
+
t.Fatal("POSIX absolute path was rejected under a non-Windows target")
|
|
109
|
+
}
|
|
110
|
+
}
|