ttsc 0.28.5 → 0.28.6
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/ttsc/api_compile.go +6 -4
- package/cmd/ttsc/api_transform.go +6 -4
- package/cmd/ttsc/build.go +7 -6
- package/cmd/ttscgraph/serve.go +70 -77
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +26 -0
- package/cmd/ttscgraph/serve_session_tracks_all_module_specifier_forms_test.go +1 -2
- package/driver/graph.go +67 -35
- package/driver/input_observation.go +305 -115
- package/driver/input_observation_test.go +203 -0
- package/driver/program.go +28 -4
- package/driver/resolution_candidates.go +247 -741
- package/driver/resolution_candidates_test.go +341 -0
- package/lib/compiler/internal/project/readProjectConfig.js +9 -7
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/runBuild.js +1 -0
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +4 -0
- package/lib/compiler/internal/sharedHostHelpers.js +11 -1
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +1 -0
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +165 -8
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/internal/projectInputPathIdentity.js +8 -7
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +57 -19
- package/package.json +8 -8
- package/shim/compiler/incremental.go +52 -0
- package/shim/compiler/resolution.go +444 -0
- package/shim/core/shim.go +15 -1
- package/src/compiler/internal/project/readProjectConfig.ts +9 -7
- package/src/compiler/internal/runBuild.ts +2 -0
- package/src/compiler/internal/sharedHostHelpers.ts +14 -0
- package/src/compiler/internal/startResidentTransform.ts +2 -0
- package/src/compiler/internal/transformProjectInMemory.ts +193 -8
- package/src/internal/projectInputPathIdentity.ts +8 -7
- package/src/structures/ITtscCompilerTransformation.ts +65 -19
- package/utility/host.go +8 -7
|
@@ -1,813 +1,319 @@
|
|
|
1
1
|
package driver
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"
|
|
5
|
-
"encoding/json"
|
|
6
|
-
"os"
|
|
7
|
-
"path/filepath"
|
|
4
|
+
"sort"
|
|
8
5
|
"strings"
|
|
9
6
|
|
|
10
|
-
"github.com/microsoft/typescript-go/shim/
|
|
11
|
-
|
|
12
|
-
"github.com/microsoft/typescript-go/shim/
|
|
7
|
+
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
8
|
+
shimtsoptions "github.com/microsoft/typescript-go/shim/tsoptions"
|
|
9
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
13
10
|
)
|
|
14
11
|
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
12
|
+
// ProgramResolutionInput is one exact lexical filesystem input consulted by
|
|
13
|
+
// TypeScript-Go while resolving a resident Program.
|
|
14
|
+
type ProgramResolutionInput struct {
|
|
15
|
+
// DirectoryEntries is true when the compiler enumerated this directory, so
|
|
16
|
+
// membership changes as well as kind and identity must invalidate it.
|
|
17
|
+
DirectoryEntries bool
|
|
18
|
+
// IdentityOnly is true only for a selected lexical alias whose contents are
|
|
19
|
+
// already owned by the resolved graph target. A link retarget must still
|
|
20
|
+
// invalidate it, while an edit to the selected bytes is not counted twice.
|
|
21
|
+
IdentityOnly bool
|
|
22
|
+
Path string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ProgramResolutionObservation is the deterministic result of replaying every
|
|
26
|
+
// resident module and type-reference resolution through TypeScript-Go itself.
|
|
27
|
+
type ProgramResolutionObservation struct {
|
|
28
|
+
// Candidates maps each source to every exact resolver input other than its
|
|
29
|
+
// identically spelled selected graph target.
|
|
30
|
+
Candidates map[string][]string
|
|
31
|
+
// Failures marks source files whose replay no longer matched the resident
|
|
32
|
+
// resolution. A universal failure is expanded to every graph source later.
|
|
33
|
+
Failures map[string]string
|
|
34
|
+
// Inputs is the flat form used by resident non-envelope hosts.
|
|
35
|
+
Inputs []ProgramResolutionInput
|
|
36
|
+
// Universal contains automatic type discovery and resolution inputs whose
|
|
37
|
+
// state can affect every source file.
|
|
38
|
+
Universal []string
|
|
39
|
+
universalFailure bool
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ObserveProgramResolutions replays the pinned TypeScript-Go resolver over a
|
|
43
|
+
// fresh observation transaction per source resolution set. This preserves the
|
|
44
|
+
// compiler's exact ordering and complete semantics without maintaining a
|
|
45
|
+
// second resolver in ttsc. Replay observations are merged into the original
|
|
46
|
+
// compiler transaction, so a state change between construction and replay
|
|
47
|
+
// becomes a proof failure.
|
|
48
|
+
func ObserveProgramResolutions(prog *Program, cwd string) ProgramResolutionObservation {
|
|
49
|
+
output := ProgramResolutionObservation{
|
|
50
|
+
Candidates: map[string][]string{},
|
|
51
|
+
Failures: map[string]string{},
|
|
52
|
+
}
|
|
53
|
+
if prog == nil || prog.TSProgram == nil || prog.FS == nil || prog.inputObserver == nil {
|
|
54
|
+
return output
|
|
55
|
+
}
|
|
56
|
+
caseSensitive := prog.FS.UseCaseSensitiveFileNames()
|
|
57
|
+
tasks := shimcompiler.ProgramResolutionTasks(prog.TSProgram)
|
|
58
|
+
sort.Slice(tasks, func(left, right int) bool {
|
|
59
|
+
a, b := tasks[left], tasks[right]
|
|
60
|
+
if a.Universal != b.Universal {
|
|
61
|
+
return !a.Universal
|
|
62
|
+
}
|
|
63
|
+
if a.SourceFile != b.SourceFile {
|
|
64
|
+
return a.SourceFile < b.SourceFile
|
|
65
|
+
}
|
|
66
|
+
if a.ContainingFile != b.ContainingFile {
|
|
67
|
+
return a.ContainingFile < b.ContainingFile
|
|
68
|
+
}
|
|
69
|
+
if a.Kind != b.Kind {
|
|
70
|
+
return a.Kind < b.Kind
|
|
71
|
+
}
|
|
72
|
+
if a.Name != b.Name {
|
|
73
|
+
return a.Name < b.Name
|
|
74
|
+
}
|
|
75
|
+
return a.Mode < b.Mode
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
if !prog.TSProgram.Options().NoResolve.IsTrue() {
|
|
79
|
+
observeProgramPathReferences(prog, cwd, caseSensitive, &output)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Automatic type discovery is a separate compiler operation that enumerates
|
|
83
|
+
// each effective type root before the individual directive resolutions.
|
|
84
|
+
// Replaying it records directory membership itself, so adding a new @types
|
|
85
|
+
// package invalidates a resident program without an importer edit.
|
|
86
|
+
automatic := newInputObservationFS(prog.FS)
|
|
87
|
+
shimcompiler.ReplayAutomaticTypeDirectiveDiscovery(prog.TSProgram, automatic)
|
|
88
|
+
output.Universal = appendResolutionPaths(output.Universal, cwd, automatic, nil, caseSensitive, &output.Inputs)
|
|
89
|
+
prog.inputObserver.mergeFrom(automatic)
|
|
90
|
+
|
|
91
|
+
for first := 0; first < len(tasks); {
|
|
92
|
+
last := first + 1
|
|
93
|
+
for last < len(tasks) && sameResolutionTaskOwner(tasks[first], tasks[last]) {
|
|
94
|
+
last++
|
|
95
|
+
}
|
|
96
|
+
group := tasks[first:last]
|
|
97
|
+
replay := newInputObservationFS(prog.FS)
|
|
98
|
+
matches := shimcompiler.ReplayProgramResolutions(group, replay)
|
|
99
|
+
selected := make([]string, 0, len(group))
|
|
100
|
+
for _, task := range group {
|
|
101
|
+
// A resolved target is already content-owned only when it became a
|
|
102
|
+
// Program source. noResolve, allowJs, depth, and diagnostic gates can
|
|
103
|
+
// leave a successful resolution outside the graph; retain those targets
|
|
104
|
+
// as resolver inputs instead of silently dropping them.
|
|
105
|
+
if task.TargetFile != "" {
|
|
106
|
+
selected = append(selected, task.TargetFile)
|
|
69
107
|
}
|
|
70
108
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// changing the old or new target bytes. An unresolved specifier keeps the full
|
|
83
|
-
// candidate list through ModuleResolutionCandidates; a resolved specifier must
|
|
84
|
-
// not track a lower-priority path because creating it cannot alter resolution.
|
|
85
|
-
func ModuleResolutionPredecessors(
|
|
86
|
-
configs []*tsoptions.ParsedCommandLine,
|
|
87
|
-
directory, cwd, specifier, resolvedFileName string,
|
|
88
|
-
caseSensitive bool,
|
|
89
|
-
context ModuleResolutionContext,
|
|
90
|
-
) []string {
|
|
91
|
-
candidates := ModuleResolutionCandidates(configs, directory, cwd, specifier, context)
|
|
92
|
-
for index, candidate := range candidates {
|
|
93
|
-
if sameCandidatePath(candidate, resolvedFileName, caseSensitive) {
|
|
94
|
-
return compactStringsInOrder(candidates[:index])
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// The winner is not spelled the way this search spells it. The compiler
|
|
98
|
-
// resolves a `node_modules` package through the real path, so its selected
|
|
99
|
-
// target can arrive symlink-resolved — and on Windows expanded out of an 8.3
|
|
100
|
-
// short name such as `RUNNER~1` — while every candidate carries the importing
|
|
101
|
-
// file's spelling. Ask the filesystem which candidate is that file instead of
|
|
102
|
-
// discarding the whole search.
|
|
103
|
-
//
|
|
104
|
-
// This second pass runs only when the first found nothing, so a program whose
|
|
105
|
-
// spellings already agree pays nothing for it.
|
|
106
|
-
for index, candidate := range candidates {
|
|
107
|
-
if sameExistingPath(candidate, resolvedFileName) {
|
|
108
|
-
end := index
|
|
109
|
-
if !sameCandidatePath(candidate, resolvedFileName, caseSensitive) {
|
|
110
|
-
end++
|
|
109
|
+
owner := group[0]
|
|
110
|
+
if owner.Universal {
|
|
111
|
+
output.Universal = appendResolutionPaths(output.Universal, cwd, replay, selected, caseSensitive, &output.Inputs)
|
|
112
|
+
if !matches {
|
|
113
|
+
output.universalFailure = true
|
|
114
|
+
}
|
|
115
|
+
} else if owner.SourceFile != "" {
|
|
116
|
+
source := TransformOutputKey(cwd, owner.SourceFile)
|
|
117
|
+
output.Candidates[source] = appendResolutionPaths(output.Candidates[source], cwd, replay, selected, caseSensitive, &output.Inputs)
|
|
118
|
+
if !matches {
|
|
119
|
+
output.Failures[source] = string(inputProofResolutionChanged)
|
|
111
120
|
}
|
|
112
|
-
return compactStringsInOrder(candidates[:end])
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// Never widen freshness to candidates whose precedence relative to the
|
|
116
|
-
// compiler's selected target we could not prove. A winner that is genuinely
|
|
117
|
-
// outside this search remains covered by its realized graph edge.
|
|
118
|
-
return nil
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
func moduleResolutionContextFor(configs []*tsoptions.ParsedCommandLine, mode shimcore.ResolutionMode) ModuleResolutionContext {
|
|
122
|
-
context := ModuleResolutionContext{Mode: mode}
|
|
123
|
-
for _, parsed := range configs {
|
|
124
|
-
if parsed != nil && parsed.ParsedConfig != nil && parsed.ParsedConfig.CompilerOptions != nil {
|
|
125
|
-
context.Options = parsed.ParsedConfig.CompilerOptions
|
|
126
|
-
break
|
|
127
121
|
}
|
|
122
|
+
prog.inputObserver.mergeFrom(replay)
|
|
123
|
+
first = last
|
|
128
124
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
func (context ModuleResolutionContext) moduleResolutionKind() shimcore.ModuleResolutionKind {
|
|
137
|
-
if context.Options == nil {
|
|
138
|
-
return shimcore.ModuleResolutionKindBundler
|
|
139
|
-
}
|
|
140
|
-
return context.Options.GetModuleResolutionKind()
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
func (context ModuleResolutionContext) usesPackageExports() bool {
|
|
144
|
-
switch context.moduleResolutionKind() {
|
|
145
|
-
case shimcore.ModuleResolutionKindNode16, shimcore.ModuleResolutionKindNodeNext:
|
|
146
|
-
return true
|
|
147
|
-
case shimcore.ModuleResolutionKindBundler:
|
|
148
|
-
return context.Options == nil || context.Options.GetResolvePackageJsonExports()
|
|
149
|
-
default:
|
|
150
|
-
return false
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
func (context ModuleResolutionContext) usesPackageImports() bool {
|
|
155
|
-
switch context.moduleResolutionKind() {
|
|
156
|
-
case shimcore.ModuleResolutionKindNode16, shimcore.ModuleResolutionKindNodeNext:
|
|
157
|
-
return true
|
|
158
|
-
case shimcore.ModuleResolutionKindBundler:
|
|
159
|
-
return context.Options == nil || context.Options.GetResolvePackageJsonImports()
|
|
160
|
-
default:
|
|
161
|
-
return false
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
func (context ModuleResolutionContext) packageConditions() map[string]struct{} {
|
|
166
|
-
if context.Options == nil {
|
|
167
|
-
return map[string]struct{}{"require": {}, "types": {}}
|
|
168
|
-
}
|
|
169
|
-
mode := context.Mode
|
|
170
|
-
if mode == shimcore.ResolutionModeNone && context.moduleResolutionKind() == shimcore.ModuleResolutionKindBundler {
|
|
171
|
-
mode = shimcore.ResolutionModeESM
|
|
172
|
-
}
|
|
173
|
-
conditions := map[string]struct{}{}
|
|
174
|
-
if mode == shimcore.ResolutionModeESM {
|
|
175
|
-
conditions["import"] = struct{}{}
|
|
176
|
-
} else {
|
|
177
|
-
conditions["require"] = struct{}{}
|
|
178
|
-
}
|
|
179
|
-
if context.Options.NoDtsResolution != shimcore.TSTrue {
|
|
180
|
-
conditions["types"] = struct{}{}
|
|
181
|
-
}
|
|
182
|
-
if context.moduleResolutionKind() != shimcore.ModuleResolutionKindBundler {
|
|
183
|
-
conditions["node"] = struct{}{}
|
|
184
|
-
}
|
|
185
|
-
for _, condition := range context.Options.CustomConditions {
|
|
186
|
-
conditions[condition] = struct{}{}
|
|
187
|
-
}
|
|
188
|
-
return conditions
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
func sameCandidatePath(left, right string, caseSensitive bool) bool {
|
|
192
|
-
left = filepath.Clean(left)
|
|
193
|
-
right = filepath.Clean(right)
|
|
194
|
-
if caseSensitive {
|
|
195
|
-
return left == right
|
|
196
|
-
}
|
|
197
|
-
return strings.EqualFold(left, right)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// sameExistingPath reports whether two spellings name the same existing
|
|
201
|
-
// filesystem object. A path that does not exist answers false, so a
|
|
202
|
-
// speculative candidate costs one failing `os.Stat` and never reaches the
|
|
203
|
-
// second call.
|
|
204
|
-
func sameExistingPath(left, right string) bool {
|
|
205
|
-
leftInfo, err := os.Stat(left)
|
|
206
|
-
if err != nil {
|
|
207
|
-
return false
|
|
208
|
-
}
|
|
209
|
-
rightInfo, err := os.Stat(right)
|
|
210
|
-
if err != nil {
|
|
211
|
-
return false
|
|
212
|
-
}
|
|
213
|
-
return os.SameFile(leftInfo, rightInfo)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// reachedWalkStop reports whether an upward `node_modules` walk has arrived at
|
|
217
|
-
// the directory it must not pass. The walk starts from the importing file's
|
|
218
|
-
// directory as the compiler spells it and stops at the project root as the
|
|
219
|
-
// caller spells it, and the two need not agree character for character on a
|
|
220
|
-
// case-insensitive or short-name-bearing filesystem. A missed stop walks every
|
|
221
|
-
// remaining ancestor up to the volume root and watches probes the compiler
|
|
222
|
-
// would never consult.
|
|
223
|
-
//
|
|
224
|
-
// Two spellings of one directory still share a final component up to case
|
|
225
|
-
// unless that component is itself shortened, so the mismatching levels — every
|
|
226
|
-
// level below the stop — are rejected without touching the filesystem. A stop
|
|
227
|
-
// missed by the remaining heuristic costs breadth, never a wrong answer: the
|
|
228
|
-
// extra ancestors are appended after the correct ones, so a predecessor list
|
|
229
|
-
// cut at the winner is unaffected.
|
|
230
|
-
func reachedWalkStop(current, stop string) bool {
|
|
231
|
-
stop = filepath.Clean(stop)
|
|
232
|
-
if current == stop {
|
|
233
|
-
return true
|
|
234
|
-
}
|
|
235
|
-
if !strings.EqualFold(filepath.Base(current), filepath.Base(stop)) {
|
|
236
|
-
return false
|
|
237
|
-
}
|
|
238
|
-
return sameExistingPath(current, stop)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// FileCandidates lists the ordered file and directory probes for base. The
|
|
242
|
-
// suffix family follows the module specifier's explicit extension, matching the
|
|
243
|
-
// resolver's extension branch instead of treating every source kind as a
|
|
244
|
-
// possible predecessor.
|
|
245
|
-
func FileCandidates(base string) []string {
|
|
246
|
-
base, suffixes := fileCandidateBaseAndSuffixes(base)
|
|
247
|
-
candidates := []string{}
|
|
248
|
-
for _, suffix := range suffixes {
|
|
249
|
-
candidates = append(candidates, base+suffix)
|
|
250
|
-
}
|
|
251
|
-
candidates = append(candidates, filepath.Join(base, "package.json"))
|
|
252
|
-
for _, suffix := range suffixes {
|
|
253
|
-
candidates = append(candidates, filepath.Join(base, "index"+suffix))
|
|
254
|
-
}
|
|
255
|
-
return candidates
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
func moduleFileCandidates(base string, context ModuleResolutionContext, includeDirectory bool) []string {
|
|
259
|
-
explicitExtension := filepath.Ext(base)
|
|
260
|
-
base, suffixes := fileCandidateBaseAndSuffixes(base)
|
|
261
|
-
if context.Options != nil && context.Options.NoDtsResolution == shimcore.TSTrue {
|
|
262
|
-
suffixes = withoutDeclarationSuffixes(suffixes)
|
|
263
|
-
}
|
|
264
|
-
if context.isESM() && explicitExtension == "" {
|
|
265
|
-
return nil
|
|
266
|
-
}
|
|
267
|
-
candidates := []string{}
|
|
268
|
-
for _, suffix := range suffixes {
|
|
269
|
-
candidates = append(candidates, moduleSuffixCandidates(base+suffix, context)...)
|
|
270
|
-
}
|
|
271
|
-
if !includeDirectory || context.isESM() {
|
|
272
|
-
return candidates
|
|
273
|
-
}
|
|
274
|
-
candidates = append(candidates, filepath.Join(base, "package.json"))
|
|
275
|
-
for _, suffix := range suffixes {
|
|
276
|
-
candidates = append(candidates, moduleSuffixCandidates(filepath.Join(base, "index"+suffix), context)...)
|
|
277
|
-
}
|
|
278
|
-
return candidates
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
func withoutDeclarationSuffixes(suffixes []string) []string {
|
|
282
|
-
output := make([]string, 0, len(suffixes))
|
|
283
|
-
for _, suffix := range suffixes {
|
|
284
|
-
if !strings.HasPrefix(suffix, ".d.") {
|
|
285
|
-
output = append(output, suffix)
|
|
125
|
+
for source, candidates := range output.Candidates {
|
|
126
|
+
compacted := compactStringsInOrder(candidates)
|
|
127
|
+
if len(compacted) == 0 {
|
|
128
|
+
delete(output.Candidates, source)
|
|
129
|
+
} else {
|
|
130
|
+
sort.Strings(compacted)
|
|
131
|
+
output.Candidates[source] = compacted
|
|
286
132
|
}
|
|
287
133
|
}
|
|
134
|
+
output.Universal = compactStringsInOrder(output.Universal)
|
|
135
|
+
sort.Strings(output.Universal)
|
|
136
|
+
output.Inputs = compactResolutionInputs(output.Inputs)
|
|
288
137
|
return output
|
|
289
138
|
}
|
|
290
139
|
|
|
291
|
-
func
|
|
292
|
-
|
|
293
|
-
return []string{path}
|
|
294
|
-
}
|
|
295
|
-
extension := filepath.Ext(path)
|
|
296
|
-
base := strings.TrimSuffix(path, extension)
|
|
297
|
-
candidates := make([]string, 0, len(context.Options.ModuleSuffixes))
|
|
298
|
-
for _, suffix := range context.Options.ModuleSuffixes {
|
|
299
|
-
candidates = append(candidates, base+suffix+extension)
|
|
300
|
-
}
|
|
301
|
-
return candidates
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
func fileCandidateBaseAndSuffixes(base string) (string, []string) {
|
|
305
|
-
lower := strings.ToLower(base)
|
|
306
|
-
switch {
|
|
307
|
-
case strings.HasSuffix(lower, ".d.mts"):
|
|
308
|
-
return base[:len(base)-len(".d.mts")], []string{".mts", ".d.mts", ".mjs"}
|
|
309
|
-
case strings.HasSuffix(lower, ".mjs"), strings.HasSuffix(lower, ".mts"):
|
|
310
|
-
return base[:len(base)-len(filepath.Ext(base))], []string{".mts", ".d.mts", ".mjs"}
|
|
311
|
-
case strings.HasSuffix(lower, ".d.cts"):
|
|
312
|
-
return base[:len(base)-len(".d.cts")], []string{".cts", ".d.cts", ".cjs"}
|
|
313
|
-
case strings.HasSuffix(lower, ".cjs"), strings.HasSuffix(lower, ".cts"):
|
|
314
|
-
return base[:len(base)-len(filepath.Ext(base))], []string{".cts", ".d.cts", ".cjs"}
|
|
315
|
-
case strings.HasSuffix(lower, ".tsx"), strings.HasSuffix(lower, ".jsx"):
|
|
316
|
-
return base[:len(base)-len(filepath.Ext(base))], []string{".tsx", ".ts", ".d.ts", ".jsx", ".js"}
|
|
317
|
-
case strings.HasSuffix(lower, ".d.ts"):
|
|
318
|
-
return base[:len(base)-len(".d.ts")], []string{".ts", ".tsx", ".d.ts", ".js", ".jsx"}
|
|
319
|
-
case strings.HasSuffix(lower, ".ts"), strings.HasSuffix(lower, ".js"):
|
|
320
|
-
return base[:len(base)-len(filepath.Ext(base))], []string{".ts", ".tsx", ".d.ts", ".js", ".jsx"}
|
|
321
|
-
default:
|
|
322
|
-
return base, []string{".ts", ".tsx", ".d.ts", ".js", ".jsx"}
|
|
323
|
-
}
|
|
140
|
+
func sameResolutionTaskOwner(left, right shimcompiler.ProgramResolutionTask) bool {
|
|
141
|
+
return left.Universal == right.Universal && left.SourceFile == right.SourceFile && (left.Universal || left.ContainingFile == right.ContainingFile)
|
|
324
142
|
}
|
|
325
143
|
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
144
|
+
// observeProgramPathReferences replays the file predicates for triple-slash
|
|
145
|
+
// path references. These use the compiler's supported-extension list directly
|
|
146
|
+
// rather than the module resolver cache covered by ProgramResolutionTasks.
|
|
147
|
+
func observeProgramPathReferences(prog *Program, cwd string, caseSensitive bool, output *ProgramResolutionObservation) {
|
|
148
|
+
supported := shimtsoptions.GetSupportedExtensions(prog.TSProgram.Options(), nil)
|
|
149
|
+
supported = shimtsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(prog.TSProgram.Options(), supported)
|
|
150
|
+
for _, source := range prog.TSProgram.SourceFiles() {
|
|
151
|
+
if source == nil || source.FileName() == "" || strings.HasPrefix(source.FileName(), bundledScheme) {
|
|
332
152
|
continue
|
|
333
153
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if strings.HasPrefix(specifier, "#") {
|
|
360
|
-
if !context.usesPackageImports() {
|
|
361
|
-
return candidates
|
|
362
|
-
}
|
|
363
|
-
for current := filepath.Clean(directory); ; current = filepath.Dir(current) {
|
|
364
|
-
fromManifest, _ := packageManifestCandidates(current, specifier, context)
|
|
365
|
-
candidates = append(candidates, fromManifest...)
|
|
366
|
-
if reachedWalkStop(current, cwd) || filepath.Dir(current) == current {
|
|
367
|
-
return candidates
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
for current := filepath.Clean(directory); ; current = filepath.Dir(current) {
|
|
372
|
-
base := filepath.Join(current, "node_modules", filepath.FromSlash(specifier))
|
|
373
|
-
root := packageRoot(base, specifier)
|
|
374
|
-
fromManifest, hasExports := packageManifestCandidates(root, packageSubpath(specifier), context)
|
|
375
|
-
// Package exports block bare file and folder lookups. Recording them would
|
|
376
|
-
// make a file that TypeScript will never select invalidate a resident
|
|
377
|
-
// snapshot, so only the export-map paths participate in this branch.
|
|
378
|
-
if !hasExports {
|
|
379
|
-
candidates = append(candidates, moduleFileCandidates(base, context, true)...)
|
|
380
|
-
}
|
|
381
|
-
candidates = append(candidates, filepath.Join(root, "package.json"))
|
|
382
|
-
candidates = append(candidates, fromManifest...)
|
|
383
|
-
if reachedWalkStop(current, cwd) || filepath.Dir(current) == current {
|
|
384
|
-
break
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
return candidates
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// SourceModuleSpecifiers returns all static and dynamic module-specifier
|
|
391
|
-
// literals a source file carries, including imports, exports, import-equals,
|
|
392
|
-
// import types, require calls, and dynamic imports.
|
|
393
|
-
func SourceModuleSpecifiers(source *ast.SourceFile) []*ast.Node {
|
|
394
|
-
if source == nil {
|
|
395
|
-
return nil
|
|
396
|
-
}
|
|
397
|
-
specifiers := []*ast.Node{}
|
|
398
|
-
var walk func(*ast.Node) bool
|
|
399
|
-
walk = func(node *ast.Node) bool {
|
|
400
|
-
if node == nil {
|
|
401
|
-
return false
|
|
402
|
-
}
|
|
403
|
-
var specifier *ast.Node
|
|
404
|
-
switch node.Kind {
|
|
405
|
-
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
|
|
406
|
-
specifier = node.AsImportDeclaration().ModuleSpecifier
|
|
407
|
-
case ast.KindExportDeclaration:
|
|
408
|
-
specifier = node.AsExportDeclaration().ModuleSpecifier
|
|
409
|
-
case ast.KindImportEqualsDeclaration:
|
|
410
|
-
reference := node.AsImportEqualsDeclaration().ModuleReference
|
|
411
|
-
if reference != nil && reference.Kind == ast.KindExternalModuleReference {
|
|
412
|
-
specifier = reference.AsExternalModuleReference().Expression
|
|
413
|
-
}
|
|
414
|
-
case ast.KindImportType:
|
|
415
|
-
argument := node.AsImportTypeNode().Argument
|
|
416
|
-
if argument != nil && argument.Kind == ast.KindLiteralType {
|
|
417
|
-
specifier = argument.AsLiteralTypeNode().Literal
|
|
418
|
-
}
|
|
419
|
-
case ast.KindCallExpression:
|
|
420
|
-
call := node.AsCallExpression()
|
|
421
|
-
if isModuleSpecifierCall(call) && call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
|
|
422
|
-
specifier = call.Arguments.Nodes[0]
|
|
154
|
+
sourceKey := TransformOutputKey(cwd, source.FileName())
|
|
155
|
+
for _, reference := range source.ReferencedFiles {
|
|
156
|
+
replay := newInputObservationFS(prog.FS)
|
|
157
|
+
selected := []string(nil)
|
|
158
|
+
for _, candidate := range pathReferenceCandidates(source.FileName(), reference.FileName, prog.TSProgram.Options().AllowNonTsExtensions.IsTrue(), supported, caseSensitive) {
|
|
159
|
+
if replay.FileExists(candidate) {
|
|
160
|
+
if resident := prog.TSProgram.GetSourceFileForResolvedModule(candidate); resident != nil {
|
|
161
|
+
selected = []string{resident.FileName()}
|
|
162
|
+
}
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
// TypeScript-Go's project-reference host can make an unbuilt output
|
|
166
|
+
// declaration exist by checking its mapped source. Replay both
|
|
167
|
+
// predicates so a later output appearance or source disappearance
|
|
168
|
+
// invalidates the same resolution without turning either spelling into
|
|
169
|
+
// a realized graph edge.
|
|
170
|
+
outputPath := shimtspath.ToPath(candidate, prog.TSProgram.GetCurrentDirectory(), caseSensitive)
|
|
171
|
+
redirect := prog.TSProgram.GetProjectReferenceFromOutputDts(outputPath)
|
|
172
|
+
if redirect == nil || !replay.FileExists(redirect.Source) {
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
if resident := prog.TSProgram.GetSourceFile(redirect.Source); resident != nil {
|
|
176
|
+
selected = []string{resident.FileName()}
|
|
177
|
+
}
|
|
178
|
+
break
|
|
423
179
|
}
|
|
180
|
+
output.Candidates[sourceKey] = appendResolutionPaths(output.Candidates[sourceKey], cwd, replay, selected, caseSensitive, &output.Inputs)
|
|
181
|
+
prog.inputObserver.mergeFrom(replay)
|
|
424
182
|
}
|
|
425
|
-
if specifier != nil && (specifier.Kind == ast.KindStringLiteral || specifier.Kind == ast.KindNoSubstitutionTemplateLiteral) {
|
|
426
|
-
specifiers = append(specifiers, specifier)
|
|
427
|
-
}
|
|
428
|
-
node.ForEachChild(walk)
|
|
429
|
-
return false
|
|
430
183
|
}
|
|
431
|
-
walk(source.AsNode())
|
|
432
|
-
return specifiers
|
|
433
184
|
}
|
|
434
185
|
|
|
435
|
-
func
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
if call.Expression.Kind == ast.KindImportKeyword {
|
|
440
|
-
return true
|
|
186
|
+
func pathReferenceCandidates(containingFile, reference string, allowNonTsExtensions bool, supported [][]string, caseSensitive bool) []string {
|
|
187
|
+
base := reference
|
|
188
|
+
if !shimtspath.IsRootedDiskPath(base) {
|
|
189
|
+
base = shimtspath.CombinePaths(shimtspath.GetDirectoryPath(containingFile), base)
|
|
441
190
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
wildcard string
|
|
448
|
-
packageEntry bool
|
|
449
|
-
usesCommonJS bool
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// packageValue preserves a package.json object's declaration order. That order
|
|
453
|
-
// is resolution semantics for conditional exports/imports, so decoding into a
|
|
454
|
-
// Go map would make the speculative prefix nondeterministic.
|
|
455
|
-
type packageValue struct {
|
|
456
|
-
array []packageValue
|
|
457
|
-
object []packageProperty
|
|
458
|
-
text *string
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
type packageProperty struct {
|
|
462
|
-
key string
|
|
463
|
-
value packageValue
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
func packageManifestCandidates(root, wildcard string, context ModuleResolutionContext) ([]string, bool) {
|
|
467
|
-
content, err := os.ReadFile(filepath.Join(root, "package.json"))
|
|
468
|
-
if err != nil {
|
|
469
|
-
return nil, false
|
|
470
|
-
}
|
|
471
|
-
var manifest struct {
|
|
472
|
-
Main string `json:"main"`
|
|
473
|
-
Module string `json:"module"`
|
|
474
|
-
Types string `json:"types"`
|
|
475
|
-
Typings string `json:"typings"`
|
|
476
|
-
Exports json.RawMessage `json:"exports"`
|
|
477
|
-
Imports json.RawMessage `json:"imports"`
|
|
478
|
-
Type string `json:"type"`
|
|
479
|
-
TypesVersions json.RawMessage `json:"typesVersions"`
|
|
480
|
-
}
|
|
481
|
-
if json.Unmarshal(content, &manifest) != nil {
|
|
482
|
-
return nil, false
|
|
483
|
-
}
|
|
484
|
-
defaultWildcard := strings.TrimPrefix(strings.TrimPrefix(wildcard, "./"), "#")
|
|
485
|
-
targets := []packageTarget{}
|
|
486
|
-
if strings.HasPrefix(wildcard, "#") {
|
|
487
|
-
if context.usesPackageImports() {
|
|
488
|
-
if value, ok := decodePackageValue(manifest.Imports); ok {
|
|
489
|
-
collectPackageMappingTargets(value, wildcard, defaultWildcard, context.packageConditions(), &targets)
|
|
490
|
-
}
|
|
191
|
+
base = shimtspath.NormalizePath(base)
|
|
192
|
+
if shimtspath.HasExtension(base) {
|
|
193
|
+
canonicalBase := shimtspath.GetCanonicalFileName(base, caseSensitive)
|
|
194
|
+
if allowNonTsExtensions || supportedFileExtension(canonicalBase, supported) {
|
|
195
|
+
return []string{base}
|
|
491
196
|
}
|
|
492
|
-
return
|
|
197
|
+
return nil
|
|
493
198
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
exportRequest = "./" + strings.TrimPrefix(wildcard, "./")
|
|
199
|
+
if allowNonTsExtensions {
|
|
200
|
+
return []string{base}
|
|
497
201
|
}
|
|
498
|
-
exports, hasExports := decodePackageValue(manifest.Exports)
|
|
499
|
-
if hasExports && context.usesPackageExports() {
|
|
500
|
-
collectPackageMappingTargets(exports, exportRequest, defaultWildcard, context.packageConditions(), &targets)
|
|
501
|
-
} else {
|
|
502
|
-
hasExports = false
|
|
503
|
-
if value, ok := decodePackageValue(manifest.TypesVersions); ok {
|
|
504
|
-
collectPackageTargets(value, defaultWildcard, nil, &targets)
|
|
505
|
-
}
|
|
506
|
-
// `typings`, `types`, and `main` are directory-entrypoint fields. A
|
|
507
|
-
// subpath resolution never falls back to them, and `module` is not a
|
|
508
|
-
// TypeScript-Go resolution field at all.
|
|
509
|
-
if wildcard == "" {
|
|
510
|
-
usesCommonJS := manifest.Type != "module"
|
|
511
|
-
if context.Options == nil || context.Options.NoDtsResolution != shimcore.TSTrue {
|
|
512
|
-
targets = append(targets,
|
|
513
|
-
packageTarget{path: manifest.Typings, wildcard: defaultWildcard, packageEntry: true, usesCommonJS: usesCommonJS},
|
|
514
|
-
packageTarget{path: manifest.Types, wildcard: defaultWildcard, packageEntry: true, usesCommonJS: usesCommonJS},
|
|
515
|
-
)
|
|
516
|
-
}
|
|
517
|
-
targets = append(targets, packageTarget{path: manifest.Main, wildcard: defaultWildcard, packageEntry: true, usesCommonJS: usesCommonJS})
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
return packageTargetCandidates(root, targets, context), hasExports
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
func packageTargetCandidates(root string, targets []packageTarget, context ModuleResolutionContext) []string {
|
|
524
202
|
candidates := []string{}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
targetPath := strings.Replace(target.path, "*", target.wildcard, 1)
|
|
530
|
-
targetContext := context
|
|
531
|
-
if target.packageEntry && target.usesCommonJS {
|
|
532
|
-
targetContext.Mode = shimcore.ResolutionModeCommonJS
|
|
203
|
+
if len(supported) != 0 {
|
|
204
|
+
for _, extension := range supported[0] {
|
|
205
|
+
candidates = append(candidates, base+extension)
|
|
533
206
|
}
|
|
534
|
-
candidates = append(candidates, moduleFileCandidates(filepath.Join(root, filepath.FromSlash(targetPath)), targetContext, target.packageEntry)...)
|
|
535
207
|
}
|
|
536
208
|
return candidates
|
|
537
209
|
}
|
|
538
210
|
|
|
539
|
-
func
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if strings.HasPrefix(property.key, ".") || strings.HasPrefix(property.key, "#") {
|
|
544
|
-
mapping = true
|
|
545
|
-
break
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
if mapping {
|
|
549
|
-
property, matched, ok := matchingPackageProperty(value.object, request)
|
|
550
|
-
if ok {
|
|
551
|
-
collectPackageTargets(property.value, matched, conditions, targets)
|
|
552
|
-
}
|
|
553
|
-
return
|
|
211
|
+
func supportedFileExtension(file string, supported [][]string) bool {
|
|
212
|
+
for _, extensions := range supported {
|
|
213
|
+
if shimtspath.FileExtensionIsOneOf(file, extensions) {
|
|
214
|
+
return true
|
|
554
215
|
}
|
|
555
216
|
}
|
|
556
|
-
|
|
217
|
+
return false
|
|
557
218
|
}
|
|
558
219
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
220
|
+
// ApplyUniversalResolutionFailure marks every source because automatic type
|
|
221
|
+
// discovery contributes to one global Program rather than to one importer.
|
|
222
|
+
func (observation ProgramResolutionObservation) ApplyUniversalResolutionFailure(sources map[string][]string) {
|
|
223
|
+
if !observation.universalFailure {
|
|
562
224
|
return
|
|
563
225
|
}
|
|
564
|
-
for
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
for _, property := range value.object {
|
|
568
|
-
if conditions == nil {
|
|
569
|
-
collectPackageTargets(property.value, wildcard, nil, targets)
|
|
570
|
-
continue
|
|
571
|
-
}
|
|
572
|
-
if property.key == "default" {
|
|
573
|
-
collectPackageTargets(property.value, wildcard, conditions, targets)
|
|
574
|
-
continue
|
|
575
|
-
}
|
|
576
|
-
if _, active := conditions[property.key]; active {
|
|
577
|
-
collectPackageTargets(property.value, wildcard, conditions, targets)
|
|
578
|
-
}
|
|
226
|
+
for source := range sources {
|
|
227
|
+
observation.Failures[source] = string(inputProofResolutionChanged)
|
|
579
228
|
}
|
|
580
229
|
}
|
|
581
230
|
|
|
582
|
-
func
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
var rawChildren []json.RawMessage
|
|
596
|
-
if json.Unmarshal(trimmed, &rawChildren) != nil {
|
|
597
|
-
return packageValue{}, false
|
|
598
|
-
}
|
|
599
|
-
value := packageValue{array: make([]packageValue, 0, len(rawChildren))}
|
|
600
|
-
for _, child := range rawChildren {
|
|
601
|
-
parsed, ok := decodePackageValue(child)
|
|
602
|
-
if ok {
|
|
603
|
-
value.array = append(value.array, parsed)
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
return value, true
|
|
607
|
-
case '{':
|
|
608
|
-
decoder := json.NewDecoder(bytes.NewReader(trimmed))
|
|
609
|
-
if _, err := decoder.Token(); err != nil {
|
|
610
|
-
return packageValue{}, false
|
|
231
|
+
func appendResolutionPaths(
|
|
232
|
+
target []string,
|
|
233
|
+
cwd string,
|
|
234
|
+
observer *inputObservationFS,
|
|
235
|
+
selected []string,
|
|
236
|
+
caseSensitive bool,
|
|
237
|
+
flat *[]ProgramResolutionInput,
|
|
238
|
+
) []string {
|
|
239
|
+
for _, candidate := range observer.observedPaths() {
|
|
240
|
+
selectedPath := slicesContainResolutionPath(selected, candidate, caseSensitive)
|
|
241
|
+
identityOnly := selectedPath && observedResolutionPathHasDistinctIdentity(observer, candidate, caseSensitive)
|
|
242
|
+
if selectedPath && !identityOnly {
|
|
243
|
+
continue
|
|
611
244
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
var child json.RawMessage
|
|
620
|
-
if decoder.Decode(&child) != nil {
|
|
621
|
-
return packageValue{}, false
|
|
622
|
-
}
|
|
623
|
-
parsed, ok := decodePackageValue(child)
|
|
624
|
-
if ok {
|
|
625
|
-
value.object = append(value.object, packageProperty{key: key, value: parsed})
|
|
245
|
+
if !identityOnly {
|
|
246
|
+
for _, target := range selected {
|
|
247
|
+
if observedResolutionAlias(observer, candidate, target, caseSensitive) {
|
|
248
|
+
identityOnly = true
|
|
249
|
+
break
|
|
250
|
+
}
|
|
626
251
|
}
|
|
627
252
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
253
|
+
*flat = append(*flat, ProgramResolutionInput{
|
|
254
|
+
DirectoryEntries: observer.observedAccessibleEntries(candidate),
|
|
255
|
+
IdentityOnly: identityOnly,
|
|
256
|
+
Path: candidate,
|
|
257
|
+
})
|
|
258
|
+
target = append(target, TransformOutputKey(cwd, candidate))
|
|
634
259
|
}
|
|
260
|
+
return target
|
|
635
261
|
}
|
|
636
262
|
|
|
637
|
-
func
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if strings.HasPrefix(specifier, "@") && len(parts) > 1 {
|
|
641
|
-
count = 2
|
|
642
|
-
}
|
|
643
|
-
if len(parts) <= count {
|
|
644
|
-
return ""
|
|
263
|
+
func observedResolutionPathHasDistinctIdentity(observer *inputObservationFS, candidate string, caseSensitive bool) bool {
|
|
264
|
+
if observer == nil || candidate == "" {
|
|
265
|
+
return false
|
|
645
266
|
}
|
|
646
|
-
|
|
267
|
+
observation, failure := observer.predicateProof(candidate)
|
|
268
|
+
return failure == "" && observation.Realpath != nil && observation.Realpath.OK && !sameResolutionPath(observation.Realpath.Path, candidate, caseSensitive)
|
|
647
269
|
}
|
|
648
270
|
|
|
649
|
-
func
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
continue
|
|
654
|
-
}
|
|
655
|
-
options := parsed.ParsedConfig.CompilerOptions
|
|
656
|
-
if options.Paths == nil {
|
|
657
|
-
continue
|
|
658
|
-
}
|
|
659
|
-
base := options.GetPathsBasePath(parsed.GetCurrentDirectory())
|
|
660
|
-
pattern := ""
|
|
661
|
-
matched := ""
|
|
662
|
-
targets := []string(nil)
|
|
663
|
-
for candidatePattern, candidateTargets := range options.Paths.Entries() {
|
|
664
|
-
if candidatePattern == specifier {
|
|
665
|
-
pattern = candidatePattern
|
|
666
|
-
matched = ""
|
|
667
|
-
targets = candidateTargets
|
|
668
|
-
break
|
|
669
|
-
}
|
|
670
|
-
candidateMatched, ok := matchPathPattern(candidatePattern, specifier)
|
|
671
|
-
if !ok || (pattern != "" && comparePatternKeys(candidatePattern, pattern) >= 0) {
|
|
672
|
-
continue
|
|
673
|
-
}
|
|
674
|
-
pattern = candidatePattern
|
|
675
|
-
matched = candidateMatched
|
|
676
|
-
targets = candidateTargets
|
|
677
|
-
}
|
|
678
|
-
if pattern == "" {
|
|
679
|
-
continue
|
|
680
|
-
}
|
|
681
|
-
for _, target := range targets {
|
|
682
|
-
target = strings.Replace(target, "*", matched, 1)
|
|
683
|
-
candidates = append(candidates, moduleFileCandidates(filepath.Join(base, filepath.FromSlash(target)), context, true)...)
|
|
271
|
+
func slicesContainResolutionPath(paths []string, candidate string, caseSensitive bool) bool {
|
|
272
|
+
for _, path := range paths {
|
|
273
|
+
if sameResolutionPath(candidate, path, caseSensitive) {
|
|
274
|
+
return true
|
|
684
275
|
}
|
|
685
276
|
}
|
|
686
|
-
return
|
|
277
|
+
return false
|
|
687
278
|
}
|
|
688
279
|
|
|
689
|
-
func
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if parsed == nil || parsed.ParsedConfig == nil || parsed.ParsedConfig.CompilerOptions == nil {
|
|
693
|
-
continue
|
|
694
|
-
}
|
|
695
|
-
roots := parsed.ParsedConfig.CompilerOptions.RootDirs
|
|
696
|
-
candidate := filepath.Clean(filepath.Join(directory, filepath.FromSlash(specifier)))
|
|
697
|
-
matchedRoot := ""
|
|
698
|
-
suffix := ""
|
|
699
|
-
for _, root := range roots {
|
|
700
|
-
relative, err := filepath.Rel(root, candidate)
|
|
701
|
-
if err != nil || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) {
|
|
702
|
-
continue
|
|
703
|
-
}
|
|
704
|
-
if len(root) > len(matchedRoot) {
|
|
705
|
-
matchedRoot = root
|
|
706
|
-
suffix = relative
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
if matchedRoot == "" {
|
|
710
|
-
continue
|
|
711
|
-
}
|
|
712
|
-
for _, targetRoot := range roots {
|
|
713
|
-
if filepath.Clean(targetRoot) == filepath.Clean(matchedRoot) {
|
|
714
|
-
continue
|
|
715
|
-
}
|
|
716
|
-
candidates = append(candidates, moduleFileCandidates(filepath.Join(targetRoot, suffix), context, true)...)
|
|
717
|
-
}
|
|
280
|
+
func sameResolutionPath(left, right string, caseSensitive bool) bool {
|
|
281
|
+
if left == "" || right == "" {
|
|
282
|
+
return false
|
|
718
283
|
}
|
|
719
|
-
return
|
|
284
|
+
return shimtspath.GetCanonicalFileName(shimtspath.NormalizePath(left), caseSensitive) == shimtspath.GetCanonicalFileName(shimtspath.NormalizePath(right), caseSensitive)
|
|
720
285
|
}
|
|
721
286
|
|
|
722
|
-
func
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
return "", pattern == specifier
|
|
726
|
-
}
|
|
727
|
-
prefix := pattern[:star]
|
|
728
|
-
suffix := pattern[star+1:]
|
|
729
|
-
if len(specifier) < len(prefix)+len(suffix) || !strings.HasPrefix(specifier, prefix) || !strings.HasSuffix(specifier, suffix) {
|
|
730
|
-
return "", false
|
|
287
|
+
func observedResolutionAlias(observer *inputObservationFS, candidate, target string, caseSensitive bool) bool {
|
|
288
|
+
if observer == nil || candidate == "" || target == "" {
|
|
289
|
+
return false
|
|
731
290
|
}
|
|
732
|
-
|
|
291
|
+
observation, failure := observer.predicateProof(candidate)
|
|
292
|
+
return failure == "" && observation.Realpath != nil && observation.Realpath.OK && sameResolutionPath(observation.Realpath.Path, target, caseSensitive)
|
|
733
293
|
}
|
|
734
294
|
|
|
735
|
-
func
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
var selected *packageProperty
|
|
742
|
-
selectedText := ""
|
|
743
|
-
for index := range properties {
|
|
744
|
-
matched, ok := matchPackagePathPattern(properties[index].key, request)
|
|
745
|
-
if !ok || (selected != nil && comparePatternKeys(properties[index].key, selected.key) >= 0) {
|
|
295
|
+
func compactResolutionInputs(inputs []ProgramResolutionInput) []ProgramResolutionInput {
|
|
296
|
+
byPath := make(map[string]ProgramResolutionInput, len(inputs))
|
|
297
|
+
for _, input := range inputs {
|
|
298
|
+
if strings.TrimSpace(input.Path) == "" {
|
|
746
299
|
continue
|
|
747
300
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
754
|
-
return *selected, selectedText, true
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
func matchPackagePathPattern(pattern, request string) (string, bool) {
|
|
758
|
-
if matched, ok := matchPathPattern(pattern, request); ok {
|
|
759
|
-
return matched, true
|
|
760
|
-
}
|
|
761
|
-
if strings.HasSuffix(pattern, "/") && strings.HasPrefix(request, pattern) {
|
|
762
|
-
return request[len(pattern):], true
|
|
763
|
-
}
|
|
764
|
-
return "", false
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
// comparePatternKeys is the TypeScript-Go resolver's pattern ordering: the
|
|
768
|
-
// longest prefix wins, then a pattern with the longest trailer. It lets the
|
|
769
|
-
// host retain candidates from exactly the one mapping the compiler selected.
|
|
770
|
-
func comparePatternKeys(left, right string) int {
|
|
771
|
-
leftStar := strings.IndexByte(left, '*')
|
|
772
|
-
rightStar := strings.IndexByte(right, '*')
|
|
773
|
-
leftBaseLength := len(left)
|
|
774
|
-
if leftStar >= 0 {
|
|
775
|
-
leftBaseLength = leftStar + 1
|
|
776
|
-
}
|
|
777
|
-
rightBaseLength := len(right)
|
|
778
|
-
if rightStar >= 0 {
|
|
779
|
-
rightBaseLength = rightStar + 1
|
|
780
|
-
}
|
|
781
|
-
switch {
|
|
782
|
-
case leftBaseLength > rightBaseLength:
|
|
783
|
-
return -1
|
|
784
|
-
case leftBaseLength < rightBaseLength:
|
|
785
|
-
return 1
|
|
786
|
-
case leftStar < 0:
|
|
787
|
-
return 1
|
|
788
|
-
case rightStar < 0:
|
|
789
|
-
return -1
|
|
790
|
-
case len(left) > len(right):
|
|
791
|
-
return -1
|
|
792
|
-
case len(left) < len(right):
|
|
793
|
-
return 1
|
|
794
|
-
default:
|
|
795
|
-
return 0
|
|
301
|
+
if previous, found := byPath[input.Path]; found {
|
|
302
|
+
input.DirectoryEntries = previous.DirectoryEntries || input.DirectoryEntries
|
|
303
|
+
input.IdentityOnly = previous.IdentityOnly && input.IdentityOnly
|
|
304
|
+
}
|
|
305
|
+
byPath[input.Path] = input
|
|
796
306
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
parts := strings.Split(filepath.ToSlash(specifier), "/")
|
|
801
|
-
count := 1
|
|
802
|
-
if strings.HasPrefix(specifier, "@") && len(parts) > 1 {
|
|
803
|
-
count = 2
|
|
307
|
+
paths := make([]string, 0, len(byPath))
|
|
308
|
+
for path := range byPath {
|
|
309
|
+
paths = append(paths, path)
|
|
804
310
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
for range
|
|
808
|
-
|
|
311
|
+
sort.Strings(paths)
|
|
312
|
+
output := make([]ProgramResolutionInput, 0, len(paths))
|
|
313
|
+
for _, path := range paths {
|
|
314
|
+
output = append(output, byPath[path])
|
|
809
315
|
}
|
|
810
|
-
return
|
|
316
|
+
return output
|
|
811
317
|
}
|
|
812
318
|
|
|
813
319
|
func compactStringsInOrder(input []string) []string {
|