ttsc 0.28.4 → 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.
Files changed (38) hide show
  1. package/cmd/ttsc/api_compile.go +6 -4
  2. package/cmd/ttsc/api_transform.go +6 -4
  3. package/cmd/ttsc/build.go +7 -6
  4. package/cmd/ttscgraph/serve.go +70 -77
  5. package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +26 -0
  6. package/cmd/ttscgraph/serve_session_tracks_all_module_specifier_forms_test.go +1 -2
  7. package/driver/graph.go +67 -35
  8. package/driver/input_observation.go +305 -115
  9. package/driver/input_observation_test.go +203 -0
  10. package/driver/program.go +28 -4
  11. package/driver/resolution_candidates.go +247 -741
  12. package/driver/resolution_candidates_test.go +341 -0
  13. package/lib/compiler/internal/project/readProjectConfig.js +9 -7
  14. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  15. package/lib/compiler/internal/runBuild.js +1 -0
  16. package/lib/compiler/internal/runBuild.js.map +1 -1
  17. package/lib/compiler/internal/sharedHostHelpers.d.ts +4 -0
  18. package/lib/compiler/internal/sharedHostHelpers.js +11 -1
  19. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  20. package/lib/compiler/internal/startResidentTransform.js +1 -0
  21. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  22. package/lib/compiler/internal/transformProjectInMemory.js +165 -8
  23. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  24. package/lib/internal/projectInputPathIdentity.js +8 -7
  25. package/lib/internal/projectInputPathIdentity.js.map +1 -1
  26. package/lib/structures/ITtscCompilerTransformation.d.ts +57 -19
  27. package/package.json +8 -8
  28. package/shim/compiler/incremental.go +52 -0
  29. package/shim/compiler/resolution.go +444 -0
  30. package/shim/core/shim.go +15 -1
  31. package/src/compiler/internal/project/readProjectConfig.ts +9 -7
  32. package/src/compiler/internal/runBuild.ts +2 -0
  33. package/src/compiler/internal/sharedHostHelpers.ts +14 -0
  34. package/src/compiler/internal/startResidentTransform.ts +2 -0
  35. package/src/compiler/internal/transformProjectInMemory.ts +193 -8
  36. package/src/internal/projectInputPathIdentity.ts +8 -7
  37. package/src/structures/ITtscCompilerTransformation.ts +65 -19
  38. package/utility/host.go +8 -7
@@ -0,0 +1,444 @@
1
+ // gen_shims:hand-maintained
2
+
3
+ package compiler
4
+
5
+ import (
6
+ "strings"
7
+ "time"
8
+
9
+ "github.com/microsoft/typescript-go/internal/ast"
10
+ "github.com/microsoft/typescript-go/internal/collections"
11
+ "github.com/microsoft/typescript-go/internal/core"
12
+ "github.com/microsoft/typescript-go/internal/module"
13
+ "github.com/microsoft/typescript-go/internal/symlinks"
14
+ "github.com/microsoft/typescript-go/internal/tsoptions"
15
+ "github.com/microsoft/typescript-go/internal/tspath"
16
+ "github.com/microsoft/typescript-go/internal/vfs"
17
+ "github.com/microsoft/typescript-go/internal/vfs/cachedvfs"
18
+ )
19
+
20
+ // ProgramResolutionKind distinguishes module and type-reference resolution.
21
+ type ProgramResolutionKind uint8
22
+
23
+ const (
24
+ ProgramResolutionKindModule ProgramResolutionKind = iota
25
+ ProgramResolutionKindTypeReference
26
+ )
27
+
28
+ // ProgramResolutionTask is one resolution already performed by a resident
29
+ // Program. The exported fields provide deterministic host ordering while the
30
+ // unexported fields retain the exact compiler context needed for replay.
31
+ type ProgramResolutionTask struct {
32
+ ContainingFile string
33
+ Kind ProgramResolutionKind
34
+ Mode core.ResolutionMode
35
+ Name string
36
+ ResolvedFile string
37
+ SourceFile string
38
+ TargetFile string
39
+ Universal bool
40
+
41
+ compilerOptions *core.CompilerOptions
42
+ currentDirectory string
43
+ expected programResolutionResult
44
+ projectReferences *projectReferenceResolutionContext
45
+ redirectedReference module.ResolvedProjectReference
46
+ }
47
+
48
+ // ProgramResolutionTasks returns every cached module and type-reference
49
+ // resolution, including unresolved entries and automatic type directives.
50
+ func ProgramResolutionTasks(program *Program) []ProgramResolutionTask {
51
+ if program == nil {
52
+ return nil
53
+ }
54
+ projectReferences := newProjectReferenceResolutionContext(program)
55
+ tasks := []ProgramResolutionTask{}
56
+ appendTask := func(kind ProgramResolutionKind, name string, mode core.ResolutionMode, filePath tspath.Path, expected programResolutionResult) {
57
+ containingFile := string(filePath)
58
+ sourceFile := ""
59
+ targetFile := ""
60
+ var redirectedReference module.ResolvedProjectReference
61
+ if source := program.GetSourceFileByPath(filePath); source != nil {
62
+ sourceFile = source.FileName()
63
+ redirectedReference, containingFile = programResolutionContext(program, source)
64
+ }
65
+ if target := program.GetSourceFileForResolvedModule(expected.resolvedFileName); target != nil {
66
+ targetFile = target.FileName()
67
+ }
68
+ tasks = append(tasks, ProgramResolutionTask{
69
+ ContainingFile: containingFile,
70
+ Kind: kind,
71
+ Mode: mode,
72
+ Name: name,
73
+ ResolvedFile: expected.resolvedFileName,
74
+ SourceFile: sourceFile,
75
+ TargetFile: targetFile,
76
+ Universal: strings.HasSuffix(containingFile, module.InferredTypesContainingFile),
77
+ compilerOptions: program.Options(),
78
+ currentDirectory: program.GetCurrentDirectory(),
79
+ expected: expected,
80
+ projectReferences: projectReferences,
81
+ redirectedReference: redirectedReference,
82
+ })
83
+ }
84
+ program.ForEachResolvedModule(func(resolution *module.ResolvedModule, name string, mode core.ResolutionMode, filePath tspath.Path) {
85
+ appendTask(ProgramResolutionKindModule, name, mode, filePath, moduleResolutionResult(resolution))
86
+ }, nil)
87
+ program.ForEachResolvedTypeReferenceDirective(func(resolution *module.ResolvedTypeReferenceDirective, name string, mode core.ResolutionMode, filePath tspath.Path) {
88
+ appendTask(ProgramResolutionKindTypeReference, name, mode, filePath, typeReferenceResolutionResult(resolution))
89
+ }, nil)
90
+ return tasks
91
+ }
92
+
93
+ // programResolutionContext mirrors projectReferenceFileMapper's containing
94
+ // file substitution using the public Program maps. The selected source path is
95
+ // part of resolution semantics, not merely diagnostic provenance.
96
+ func programResolutionContext(program *Program, source ast.HasFileName) (module.ResolvedProjectReference, string) {
97
+ if redirected := program.GetProjectReferenceFromSource(source.Path()); redirected != nil {
98
+ return redirected.Resolved, redirected.Source
99
+ }
100
+ if redirected := program.GetProjectReferenceFromOutputDts(source.Path()); redirected != nil {
101
+ return redirected.Resolved, redirected.Source
102
+ }
103
+ redirect := program.GetRedirectForResolution(source)
104
+ if redirect == nil {
105
+ return nil, source.FileName()
106
+ }
107
+ // The remaining redirect form is a preserved node_modules symlink whose
108
+ // physical declaration belongs to a project reference. Resolve the same
109
+ // physical key the compiler mapper used and retain the original source name.
110
+ realpath := program.Host().FS().Realpath(source.FileName())
111
+ path := tspath.ToPath(realpath, program.GetCurrentDirectory(), program.UseCaseSensitiveFileNames())
112
+ if redirected := program.GetProjectReferenceFromOutputDts(path); redirected != nil {
113
+ return redirected.Resolved, redirected.Source
114
+ }
115
+ // A concurrent retarget can make the public lookup disappear after the
116
+ // resident redirect was cached. Keep the redirect so replay necessarily
117
+ // disagrees with the resident result or its observed identity proof fails.
118
+ return redirect, source.FileName()
119
+ }
120
+
121
+ // ReplayProgramResolutions resolves one source's tasks with one fresh upstream
122
+ // resolver and reports whether every result still matches the resident Program.
123
+ func ReplayProgramResolutions(tasks []ProgramResolutionTask, filesystem vfs.FS) bool {
124
+ if len(tasks) == 0 || filesystem == nil || tasks[0].compilerOptions == nil {
125
+ return false
126
+ }
127
+ first := tasks[0]
128
+ host := resolutionHost{
129
+ filesystem: first.projectReferences.filesystem(filesystem),
130
+ currentDirectory: first.currentDirectory,
131
+ }
132
+ resolver := module.NewResolver(host, first.compilerOptions, "", "")
133
+ matches := true
134
+ for _, task := range tasks {
135
+ var actual programResolutionResult
136
+ switch task.Kind {
137
+ case ProgramResolutionKindModule:
138
+ resolution, _ := resolver.ResolveModuleName(task.Name, task.ContainingFile, task.Mode, task.redirectedReference)
139
+ actual = moduleResolutionResult(resolution)
140
+ case ProgramResolutionKindTypeReference:
141
+ resolution, _ := resolver.ResolveTypeReferenceDirective(task.Name, task.ContainingFile, task.Mode, task.redirectedReference)
142
+ actual = typeReferenceResolutionResult(resolution)
143
+ default:
144
+ matches = false
145
+ continue
146
+ }
147
+ if actual != task.expected {
148
+ matches = false
149
+ }
150
+ }
151
+ return matches
152
+ }
153
+
154
+ // projectReferenceResolutionContext snapshots the immutable metadata needed to
155
+ // recreate fileLoader's project-reference declaration view for every replay
156
+ // filesystem. A referenced output declaration may be absent on disk while its
157
+ // source exists; the resolver must see the virtual declaration before Program
158
+ // substitutes that source into the loaded graph.
159
+ type projectReferenceResolutionContext struct {
160
+ currentDirectory string
161
+ dtsDirectories collections.Set[tspath.Path]
162
+ outputDtsToProjectReference map[tspath.Path]*tsoptions.SourceOutputAndProjectReference
163
+ }
164
+
165
+ func newProjectReferenceResolutionContext(program *Program) *projectReferenceResolutionContext {
166
+ if program == nil {
167
+ return nil
168
+ }
169
+ outputDtsToProjectReference := map[tspath.Path]*tsoptions.SourceOutputAndProjectReference{}
170
+ dtsDirectories := collections.Set[tspath.Path]{}
171
+ useSourceOfProjectReference := false
172
+ program.RangeResolvedProjectReference(func(_ tspath.Path, config *tsoptions.ParsedCommandLine, _ *tsoptions.ParsedCommandLine, _ int) bool {
173
+ if config == nil {
174
+ return true
175
+ }
176
+ config.ParseInputOutputNames()
177
+ for path := range config.SourceToProjectReference() {
178
+ useSourceOfProjectReference = useSourceOfProjectReference || program.IsSourceFromProjectReference(path)
179
+ }
180
+ for path, reference := range config.OutputDtsToProjectReference() {
181
+ outputDtsToProjectReference[path] = reference
182
+ }
183
+ declarationDirectory := config.CompilerOptions().DeclarationDir
184
+ if declarationDirectory == "" {
185
+ declarationDirectory = config.CompilerOptions().OutDir
186
+ }
187
+ if declarationDirectory != "" {
188
+ dtsDirectories.Add(tspath.ToPath(declarationDirectory, program.GetCurrentDirectory(), program.UseCaseSensitiveFileNames()))
189
+ }
190
+ return true
191
+ })
192
+ if !useSourceOfProjectReference || len(outputDtsToProjectReference) == 0 {
193
+ return nil
194
+ }
195
+ return &projectReferenceResolutionContext{
196
+ currentDirectory: program.GetCurrentDirectory(),
197
+ dtsDirectories: dtsDirectories,
198
+ outputDtsToProjectReference: outputDtsToProjectReference,
199
+ }
200
+ }
201
+
202
+ func (context *projectReferenceResolutionContext) filesystem(filesystem vfs.FS) vfs.FS {
203
+ if context == nil || filesystem == nil {
204
+ return filesystem
205
+ }
206
+ return cachedvfs.From(&projectReferenceResolutionFS{
207
+ filesystem: filesystem,
208
+ currentDirectory: context.currentDirectory,
209
+ dtsDirectories: context.dtsDirectories,
210
+ knownSymlinks: symlinks.KnownSymlinks{},
211
+ outputDtsToProjectReference: context.outputDtsToProjectReference,
212
+ })
213
+ }
214
+
215
+ // projectReferenceResolutionFS mirrors TypeScript-Go's
216
+ // projectReferenceDtsFakingVfs over the caller's observation filesystem.
217
+ type projectReferenceResolutionFS struct {
218
+ filesystem vfs.FS
219
+ currentDirectory string
220
+ dtsDirectories collections.Set[tspath.Path]
221
+ knownSymlinks symlinks.KnownSymlinks
222
+ outputDtsToProjectReference map[tspath.Path]*tsoptions.SourceOutputAndProjectReference
223
+ }
224
+
225
+ var _ vfs.FS = (*projectReferenceResolutionFS)(nil)
226
+
227
+ func (fs *projectReferenceResolutionFS) UseCaseSensitiveFileNames() bool {
228
+ return fs.filesystem.UseCaseSensitiveFileNames()
229
+ }
230
+
231
+ func (fs *projectReferenceResolutionFS) FileExists(path string) bool {
232
+ if fs.filesystem.FileExists(path) {
233
+ return true
234
+ }
235
+ if !tspath.IsDeclarationFileName(path) {
236
+ return false
237
+ }
238
+ return fs.fileOrDirectoryExistsUsingSource(path, true)
239
+ }
240
+
241
+ func (fs *projectReferenceResolutionFS) ReadFile(path string) (string, bool) {
242
+ return fs.filesystem.ReadFile(path)
243
+ }
244
+
245
+ func (fs *projectReferenceResolutionFS) WriteFile(string, string) error {
246
+ panic("should not be called by resolver")
247
+ }
248
+
249
+ func (fs *projectReferenceResolutionFS) AppendFile(string, string) error {
250
+ panic("should not be called by resolver")
251
+ }
252
+
253
+ func (fs *projectReferenceResolutionFS) Remove(string) error {
254
+ panic("should not be called by resolver")
255
+ }
256
+
257
+ func (fs *projectReferenceResolutionFS) Chtimes(string, time.Time, time.Time) error {
258
+ panic("should not be called by resolver")
259
+ }
260
+
261
+ func (fs *projectReferenceResolutionFS) DirectoryExists(path string) bool {
262
+ if fs.filesystem.DirectoryExists(path) {
263
+ fs.handleDirectoryCouldBeSymlink(path)
264
+ return true
265
+ }
266
+ return fs.fileOrDirectoryExistsUsingSource(path, false)
267
+ }
268
+
269
+ func (fs *projectReferenceResolutionFS) GetAccessibleEntries(string) vfs.Entries {
270
+ panic("should not be called by resolver")
271
+ }
272
+
273
+ func (fs *projectReferenceResolutionFS) Stat(string) vfs.FileInfo {
274
+ panic("should not be called by resolver")
275
+ }
276
+
277
+ func (fs *projectReferenceResolutionFS) WalkDir(string, vfs.WalkDirFunc) error {
278
+ panic("should not be called by resolver")
279
+ }
280
+
281
+ func (fs *projectReferenceResolutionFS) Realpath(path string) string {
282
+ if result, ok := fs.knownSymlinks.Files().Load(fs.toPath(path)); ok {
283
+ return result
284
+ }
285
+ return fs.filesystem.Realpath(path)
286
+ }
287
+
288
+ func (fs *projectReferenceResolutionFS) toPath(path string) tspath.Path {
289
+ return tspath.ToPath(path, fs.currentDirectory, fs.UseCaseSensitiveFileNames())
290
+ }
291
+
292
+ func (fs *projectReferenceResolutionFS) handleDirectoryCouldBeSymlink(directory string) {
293
+ if tspath.ContainsIgnoredPath(directory) || !strings.Contains(directory, "/node_modules/") {
294
+ return
295
+ }
296
+ directoryPath := tspath.Path(tspath.EnsureTrailingDirectorySeparator(string(fs.toPath(directory))))
297
+ if _, ok := fs.knownSymlinks.Directories().Load(directoryPath); ok {
298
+ return
299
+ }
300
+ realDirectory := fs.Realpath(directory)
301
+ if realDirectory == directory {
302
+ return
303
+ }
304
+ realPath := tspath.Path(tspath.EnsureTrailingDirectorySeparator(string(fs.toPath(realDirectory))))
305
+ if realPath == directoryPath {
306
+ return
307
+ }
308
+ fs.knownSymlinks.SetDirectory(directory, directoryPath, &symlinks.KnownDirectoryLink{
309
+ Real: tspath.EnsureTrailingDirectorySeparator(realDirectory),
310
+ RealPath: realPath,
311
+ })
312
+ }
313
+
314
+ func (fs *projectReferenceResolutionFS) fileOrDirectoryExistsUsingSource(fileOrDirectory string, isFile bool) bool {
315
+ existence := fs.directoryExistsIfProjectReferenceDeclarationDirectory
316
+ if isFile {
317
+ existence = fs.fileExistsIfProjectReferenceDeclaration
318
+ }
319
+ result := existence(fileOrDirectory)
320
+ if result != core.TSUnknown {
321
+ return result == core.TSTrue
322
+ }
323
+ knownDirectoryLinks := fs.knownSymlinks.Directories()
324
+ if knownDirectoryLinks.Size() == 0 {
325
+ return false
326
+ }
327
+ fileOrDirectoryPath := fs.toPath(fileOrDirectory)
328
+ if !strings.Contains(string(fileOrDirectoryPath), "/node_modules/") {
329
+ return false
330
+ }
331
+ if isFile {
332
+ if _, ok := fs.knownSymlinks.Files().Load(fileOrDirectoryPath); ok {
333
+ return true
334
+ }
335
+ }
336
+ exists := false
337
+ knownDirectoryLinks.Range(func(directoryPath tspath.Path, knownDirectoryLink *symlinks.KnownDirectoryLink) bool {
338
+ relative, hasPrefix := strings.CutPrefix(string(fileOrDirectoryPath), string(directoryPath))
339
+ if !hasPrefix {
340
+ return true
341
+ }
342
+ if exists = existence(string(knownDirectoryLink.RealPath) + relative).IsTrue(); !exists {
343
+ return true
344
+ }
345
+ if isFile {
346
+ absolutePath := tspath.GetNormalizedAbsolutePath(fileOrDirectory, fs.currentDirectory)
347
+ fs.knownSymlinks.SetFile(
348
+ absolutePath,
349
+ fileOrDirectoryPath,
350
+ knownDirectoryLink.Real+absolutePath[len(directoryPath):],
351
+ )
352
+ }
353
+ return false
354
+ })
355
+ return exists
356
+ }
357
+
358
+ func (fs *projectReferenceResolutionFS) fileExistsIfProjectReferenceDeclaration(file string) core.Tristate {
359
+ reference := fs.outputDtsToProjectReference[fs.toPath(file)]
360
+ if reference == nil {
361
+ return core.TSUnknown
362
+ }
363
+ return core.IfElse(fs.filesystem.FileExists(reference.Source), core.TSTrue, core.TSFalse)
364
+ }
365
+
366
+ func (fs *projectReferenceResolutionFS) directoryExistsIfProjectReferenceDeclarationDirectory(directory string) core.Tristate {
367
+ directoryPath := fs.toPath(directory)
368
+ directoryPathWithSeparator := directoryPath + "/"
369
+ for declarationDirectoryPath := range fs.dtsDirectories.Keys() {
370
+ if directoryPath == declarationDirectoryPath || strings.HasPrefix(string(declarationDirectoryPath), string(directoryPathWithSeparator)) || strings.HasPrefix(string(directoryPath), string(declarationDirectoryPath)+"/") {
371
+ return core.TSTrue
372
+ }
373
+ }
374
+ return core.TSUnknown
375
+ }
376
+
377
+ // ReplayAutomaticTypeDirectiveDiscovery repeats the compiler's exact wildcard
378
+ // type-root enumeration over filesystem so a host can observe its inputs.
379
+ func ReplayAutomaticTypeDirectiveDiscovery(program *Program, filesystem vfs.FS) {
380
+ if program == nil || filesystem == nil || program.Options() == nil {
381
+ return
382
+ }
383
+ module.GetAutomaticTypeDirectiveNames(program.Options(), resolutionHost{
384
+ filesystem: filesystem,
385
+ currentDirectory: program.GetCurrentDirectory(),
386
+ })
387
+ }
388
+
389
+ type resolutionHost struct {
390
+ filesystem vfs.FS
391
+ currentDirectory string
392
+ }
393
+
394
+ func (host resolutionHost) FS() vfs.FS { return host.filesystem }
395
+
396
+ func (host resolutionHost) GetCurrentDirectory() string { return host.currentDirectory }
397
+
398
+ type programResolutionResult struct {
399
+ alternateResult string
400
+ extension string
401
+ isExternalLibraryImport bool
402
+ originalPath string
403
+ packageName string
404
+ packagePeerDependencies string
405
+ packageSubModuleName string
406
+ packageVersion string
407
+ primary bool
408
+ resolvedFileName string
409
+ resolvedUsingTsExtension bool
410
+ }
411
+
412
+ func moduleResolutionResult(resolution *module.ResolvedModule) programResolutionResult {
413
+ if resolution == nil {
414
+ return programResolutionResult{}
415
+ }
416
+ return programResolutionResult{
417
+ alternateResult: resolution.AlternateResult,
418
+ extension: resolution.Extension,
419
+ isExternalLibraryImport: resolution.IsExternalLibraryImport,
420
+ originalPath: resolution.OriginalPath,
421
+ packageName: resolution.PackageId.Name,
422
+ packagePeerDependencies: resolution.PackageId.PeerDependencies,
423
+ packageSubModuleName: resolution.PackageId.SubModuleName,
424
+ packageVersion: resolution.PackageId.Version,
425
+ resolvedFileName: resolution.ResolvedFileName,
426
+ resolvedUsingTsExtension: resolution.ResolvedUsingTsExtension,
427
+ }
428
+ }
429
+
430
+ func typeReferenceResolutionResult(resolution *module.ResolvedTypeReferenceDirective) programResolutionResult {
431
+ if resolution == nil {
432
+ return programResolutionResult{}
433
+ }
434
+ return programResolutionResult{
435
+ isExternalLibraryImport: resolution.IsExternalLibraryImport,
436
+ originalPath: resolution.OriginalPath,
437
+ packageName: resolution.PackageId.Name,
438
+ packagePeerDependencies: resolution.PackageId.PeerDependencies,
439
+ packageSubModuleName: resolution.PackageId.SubModuleName,
440
+ packageVersion: resolution.PackageId.Version,
441
+ primary: resolution.Primary,
442
+ resolvedFileName: resolution.ResolvedFileName,
443
+ }
444
+ }
package/shim/core/shim.go CHANGED
@@ -6,7 +6,10 @@
6
6
  // primitives without exposing the full internal surface.
7
7
  package core
8
8
 
9
- import innercore "github.com/microsoft/typescript-go/internal/core"
9
+ import (
10
+ innercore "github.com/microsoft/typescript-go/internal/core"
11
+ innersemver "github.com/microsoft/typescript-go/internal/semver"
12
+ )
10
13
 
11
14
  // CompilerOptions holds the parsed tsconfig compiler options passed to the
12
15
  // TypeScript-Go program host.
@@ -96,6 +99,17 @@ const (
96
99
  // consumer can tell which checker resolved the facts it is reading.
97
100
  func Version() string { return innercore.Version() }
98
101
 
102
+ // TypeScriptVersionSatisfiesRange reports whether the compiler's own version
103
+ // satisfies a typesVersions range under TypeScript-Go's semver grammar.
104
+ func TypeScriptVersionSatisfiesRange(text string) bool {
105
+ versionRange, ok := innersemver.TryParseVersionRange(text)
106
+ if !ok {
107
+ return false
108
+ }
109
+ version, err := innersemver.TryParseVersion(innercore.Version())
110
+ return err == nil && versionRange.Test(&version)
111
+ }
112
+
99
113
  // ComputeECMALineStarts applies the compiler's LF, CRLF, CR, LS, and PS line
100
114
  // model to UTF-8 source text.
101
115
  func ComputeECMALineStarts(text string) ECMALineStarts {
@@ -279,11 +279,12 @@ function resolveCompilerOptionPath(
279
279
  configDir: string,
280
280
  value: string,
281
281
  ): string {
282
+ const normalized = value.replaceAll("\\", "/");
282
283
  if (
283
- value.slice(0, CONFIG_DIR_TEMPLATE.length).toLowerCase() !==
284
+ normalized.slice(0, CONFIG_DIR_TEMPLATE.length).toLowerCase() !==
284
285
  CONFIG_DIR_TEMPLATE.toLowerCase()
285
286
  ) {
286
- return resolveAbsolutePath(declaringDir, value);
287
+ return resolveAbsolutePath(declaringDir, normalized);
287
288
  }
288
289
  // The compiler picks the base directory from a case-insensitive prefix but
289
290
  // substitutes only the exact spelling, so a mis-cased template still anchors
@@ -294,10 +295,10 @@ function resolveCompilerOptionPath(
294
295
  // The exact spelling becomes "./" before the result is normalized, which
295
296
  // keeps the value relative even when its remainder resembles an absolute
296
297
  // Windows path. Either config-file separator is accepted on every host.
297
- const substituted = value.startsWith(CONFIG_DIR_TEMPLATE)
298
- ? `./${value.slice(CONFIG_DIR_TEMPLATE.length)}`
299
- : value;
300
- return resolveAbsolutePath(configDir, substituted.replaceAll("\\", "/"));
298
+ const substituted = normalized.startsWith(CONFIG_DIR_TEMPLATE)
299
+ ? `./${normalized.slice(CONFIG_DIR_TEMPLATE.length)}`
300
+ : normalized;
301
+ return resolveAbsolutePath(configDir, substituted);
301
302
  }
302
303
 
303
304
  /**
@@ -311,6 +312,7 @@ function resolveCompilerOptionPath(
311
312
  * also tries appending `.json` when the first attempt fails).
312
313
  */
313
314
  function resolveExtendsConfig(tsconfig: string, specifier: string): string {
315
+ specifier = specifier.replaceAll("\\", "/");
314
316
  const baseDir = path.dirname(tsconfig);
315
317
  if (path.isAbsolute(specifier)) {
316
318
  return resolveExistingExtendsPath(specifier);
@@ -387,7 +389,7 @@ function resolvePackageManifestTsconfig(
387
389
  if (typeof field !== "string" || field.length === 0) {
388
390
  return undefined;
389
391
  }
390
- return path.resolve(path.dirname(manifestPath), field);
392
+ return path.resolve(path.dirname(manifestPath), field.replaceAll("\\", "/"));
391
393
  }
392
394
 
393
395
  /**
@@ -29,6 +29,7 @@ import { resolveTsgo } from "./resolveTsgo";
29
29
  import {
30
30
  TSGO_ARGS_ENV,
31
31
  assertSharedHostCompatibility,
32
+ clearInheritedSemanticConfigPath,
32
33
  clearInheritedTsgoArgs,
33
34
  inheritedSidecarEnv,
34
35
  linkedTransformPlugins,
@@ -230,6 +231,7 @@ function nativePluginEnv(
230
231
  } else {
231
232
  clearInheritedTsgoArgs(env, extra);
232
233
  }
234
+ clearInheritedSemanticConfigPath(env, extra);
233
235
  // The anchor is per-invocation state owned by this host: when this run
234
236
  // declared none (and the caller's env does not name one), drop any value
235
237
  // inherited from an ancestor ttsc process so a nested build never
@@ -9,6 +9,9 @@ import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoa
9
9
  */
10
10
  export const TSGO_ARGS_ENV = "TTSC_TSGO_ARGS";
11
11
 
12
+ /** Internal channel preserving a generated wrapper's user-authored config owner. */
13
+ export const SEMANTIC_CONFIG_PATH_ENV = "TTSC_SEMANTIC_CONFIG_PATH";
14
+
12
15
  /**
13
16
  * Drop a forwarded-tsgo payload this invocation did not publish itself.
14
17
  *
@@ -29,6 +32,16 @@ export function clearInheritedTsgoArgs(
29
32
  }
30
33
  }
31
34
 
35
+ /** Drop an outer generated wrapper's config owner from an unrelated child run. */
36
+ export function clearInheritedSemanticConfigPath(
37
+ env: NodeJS.ProcessEnv,
38
+ callerEnv: NodeJS.ProcessEnv | undefined,
39
+ ): void {
40
+ if (callerEnv?.[SEMANTIC_CONFIG_PATH_ENV] === undefined) {
41
+ delete env[SEMANTIC_CONFIG_PATH_ENV];
42
+ }
43
+ }
44
+
32
45
  /**
33
46
  * The `{ ...process.env, ...callerEnv }` a child process inherits, minus a
34
47
  * forwarded-tsgo payload this lane never published.
@@ -43,6 +56,7 @@ export function inheritedSidecarEnv(
43
56
  ): NodeJS.ProcessEnv {
44
57
  const env: NodeJS.ProcessEnv = { ...process.env, ...callerEnv };
45
58
  clearInheritedTsgoArgs(env, callerEnv);
59
+ clearInheritedSemanticConfigPath(env, callerEnv);
46
60
  return env;
47
61
  }
48
62
 
@@ -10,6 +10,7 @@ import { resolveBinary } from "./resolveBinary";
10
10
  import { resolveTsgo } from "./resolveTsgo";
11
11
  import {
12
12
  assertSharedHostCompatibility,
13
+ clearInheritedSemanticConfigPath,
13
14
  clearInheritedTsgoArgs,
14
15
  inheritedSidecarEnv,
15
16
  linkedTransformPlugins,
@@ -130,6 +131,7 @@ function residentEnv(
130
131
  // This lane forwards no tsgo argv of its own, so anything inherited belongs
131
132
  // to an outer ttsc run and must not reach the resident sidecar.
132
133
  clearInheritedTsgoArgs(env, context.env);
134
+ clearInheritedSemanticConfigPath(env, context.env);
133
135
  const linked = linkedTransformPlugins(nativePlugins);
134
136
  if (linked.length !== 0) {
135
137
  env.TTSC_LINKED_PLUGINS_JSON = serializeNativePlugins(linked);