ttsc 0.34.0 → 0.34.2

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 (29) hide show
  1. package/cmd/ttsc/api_transform.go +13 -3
  2. package/cmd/ttsc/api_transform_config_recovery_test.go +44 -0
  3. package/driver/check_observations.go +24 -2
  4. package/driver/graph.go +40 -3
  5. package/driver/input_observation.go +16 -0
  6. package/driver/program.go +41 -15
  7. package/driver/recovery_graph.go +25 -0
  8. package/driver/target_ts70.go +12 -0
  9. package/driver/target_ts71.go +12 -0
  10. package/driver/transform_dependencies.go +22 -1
  11. package/driver/transform_membership.go +61 -0
  12. package/lib/compiler/internal/build/appendBuildOutput.d.ts +1 -1
  13. package/lib/compiler/internal/build/appendBuildOutput.js +37 -5
  14. package/lib/compiler/internal/build/appendBuildOutput.js.map +1 -1
  15. package/lib/compiler/internal/parseNativeTransformOutput.js +31 -1
  16. package/lib/compiler/internal/parseNativeTransformOutput.js.map +1 -1
  17. package/lib/internal/CompilerResultAdapter.d.ts +7 -5
  18. package/lib/internal/CompilerResultAdapter.js +29 -6
  19. package/lib/internal/CompilerResultAdapter.js.map +1 -1
  20. package/lib/structures/ITtscCompilerTransformation.d.ts +25 -1
  21. package/package.json +8 -8
  22. package/shim/compiler/neutral.go +14 -0
  23. package/src/compiler/internal/build/appendBuildOutput.ts +42 -5
  24. package/src/compiler/internal/parseNativeTransformOutput.ts +45 -1
  25. package/src/internal/CompilerResultAdapter.ts +29 -6
  26. package/src/structures/ITtscCompilerTransformation.ts +28 -1
  27. package/targets/7.1/bridge/compiler/neutral.go +39 -4
  28. package/targets/7.1/bridge/compiler/resolution.go +14 -5
  29. package/utility/host.go +19 -6
@@ -217,15 +217,17 @@ function toCompilerResult(project: ProjectResult): ITtscCompilerResult {
217
217
  }
218
218
 
219
219
  /**
220
- * Adapt source/build outcome and copy optional advisory references only when
221
- * present.
220
+ * Adapt source/build outcome and preserve optional advisory values. Mapper
221
+ * project inputs join each source's declared dependencies on success and
222
+ * failure; they do not become compiler-read observations or completeness
223
+ * certificates.
222
224
  *
223
- * @evidence contracts/common.md#principled-implementation Undefined advisory fields are omitted; present empty/false values and references survive both success and failure, alongside the existing diagnostic decision.
225
+ * @evidence contracts/common.md#principled-implementation Undefined advisory fields are omitted and present empty/false values survive both success and failure. Universal mapper declarations join source dependencies with own-key safety; compiler completeness and read witnesses retain their independent meaning.
224
226
  * @evidence contracts/common.md#clear-and-simple-design One optional-field projection is shared by the two result branches.
225
227
  * @evidence contracts/common.md#prohibited-implementation-shortcuts The adapter does not manufacture graph, host-read or completeness proof and does not discard it solely because the build failed.
226
- * @evidence contracts/common.md#meaningful-documentation The native sentence states omission policy and reference ownership.
228
+ * @evidence contracts/common.md#meaningful-documentation Native prose distinguishes mapper declarations from read proof and completeness on both result branches.
227
229
  * @evidenceExclude contracts/portability.md#os-neutral-implementation Advisory values are passed through without native path normalization or capability inference.
228
- * @evidence contracts/performance.md#efficient-algorithms Fixed optional-field checks retain references without scanning map text; diagnostic classification and empty-failure formatting have their delegated data costs.
230
+ * @evidence contracts/performance.md#efficient-algorithms Optional fields retain references unless mapper inputs require a per-source Set union. Work and copied storage follow source keys plus each source's dependency/input text; diagnostic classification and failure formatting keep their delegated costs.
229
231
  * @evidenceExclude contracts/performance.md#reuse-equivalent-work This adaptation coordinates no reusable compiler/worker or outcome cache.
230
232
  * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Returned source/advisory objects remain reachable under caller ownership; this operation owns no process, lease or historical registry.
231
233
  */
@@ -233,7 +235,7 @@ export function toCompilerTransformation(
233
235
  project: ProjectTransformation,
234
236
  ): ITtscCompilerTransformation {
235
237
  const {
236
- dependencies,
238
+ dependencies: reportedDependencies,
237
239
  dependenciesComplete,
238
240
  graph,
239
241
  hostInputHashes,
@@ -247,6 +249,27 @@ export function toCompilerTransformation(
247
249
  typescript,
248
250
  volatile,
249
251
  } = project;
252
+ let dependencies = reportedDependencies;
253
+ if (graph?.contentMapperInputs?.length) {
254
+ const keys = new Set([
255
+ ...Object.keys(typescript),
256
+ ...Object.keys(graph.edges),
257
+ ]);
258
+ dependencies = Object.fromEntries([
259
+ ...Object.entries(dependencies ?? {}).filter(([key]) => !keys.has(key)),
260
+ ...Array.from(keys, (key) => [
261
+ key,
262
+ [
263
+ ...new Set([
264
+ ...(Object.hasOwn(dependencies ?? {}, key)
265
+ ? dependencies![key]!
266
+ : []),
267
+ ...graph.contentMapperInputs!,
268
+ ]),
269
+ ].filter((input) => input !== key),
270
+ ]),
271
+ ]);
272
+ }
250
273
  const advisoryFields = {
251
274
  ...(dependencies === undefined ? {} : { dependencies }),
252
275
  ...(dependenciesComplete === undefined ? {} : { dependenciesComplete }),
@@ -51,6 +51,23 @@ export namespace ITtscCompilerTransformation {
51
51
  * @evidence contracts/portability.md#os-neutral-implementation Graph keys use the producer's cwd-relative slash spelling with absolute slash paths for outside-root or cross-volume inputs; this protocol spelling is distinct from reported native realpaths carried by inputRealpaths and observations, which can retain lexical fallback on native VFS errors. The reported compiler case policy governs compiler membership comparisons rather than an OS-name assumption; absent policy or proof does not establish native filesystem capabilities.
52
52
  */
53
53
  export interface IReferenceGraph {
54
+ /**
55
+ * Effective native possible-root suffixes and output-directory exclusions.
56
+ * A report that differs from a host's provisional policy requires a new
57
+ * membership walk. Directory paths are absolute; null clears an option.
58
+ * Authored files/include and explicit excludes remain configuration
59
+ * inputs.
60
+ */
61
+ membership?: {
62
+ /** Original suffix spellings admitted by the compiler and its mappers. */
63
+ inputExtensions: string[];
64
+
65
+ /** Effective absolute output directory, or null when unset. */
66
+ outDir: string | null;
67
+
68
+ /** Effective absolute declaration directory, or null when unset. */
69
+ declarationDir: string | null;
70
+ };
54
71
  /**
55
72
  * Direct resolved references per file: imports, re-exports, `///
56
73
  * <reference>` targets, and type reference directives — type-only edges
@@ -82,10 +99,20 @@ export namespace ITtscCompilerTransformation {
82
99
 
83
100
  /**
84
101
  * Automatic type discovery and resolution inputs that affect every source,
85
- * including effective type-root directory membership.
102
+ * including compiler response files and effective type-root directory
103
+ * membership. Failed loads can retain their observed CLI/config recovery
104
+ * paths here without a completed Program or source population.
86
105
  */
87
106
  resolutionInputs?: string[];
88
107
 
108
+ /**
109
+ * Mapper package manifests and dynamic project watchedFiles. These are
110
+ * declared dependencies for every output, including files whose plugin
111
+ * dependencies are complete. They carry no compiler-read proof: consumers
112
+ * must witness them before compilation and compare afterward.
113
+ */
114
+ contentMapperInputs?: string[];
115
+
89
116
  /**
90
117
  * Filesystem predicates recorded during construction and resolver replay
91
118
  * for this graph. Each property is independent: `fileExists: false` and
@@ -3,6 +3,8 @@ package compiler
3
3
  import (
4
4
  "context"
5
5
  "errors"
6
+ "path/filepath"
7
+ "sort"
6
8
  "sync"
7
9
 
8
10
  innerast "github.com/microsoft/TypeScript/tsc/internal/ast"
@@ -52,7 +54,7 @@ func NewHost(currentDirectory innertspath.RootedDirectoryPath, fs innervfs.FS, d
52
54
  // @evidence contracts/common.md#prohibited-implementation-shortcuts The compiler's own runExternalCode gate and mapper resolution decide whether mappers run; no ttsc authorization or mapping policy is added.
53
55
  // @evidence contracts/common.md#meaningful-documentation Native prose states when mappers run, the nil meaning and the 7.0 difference.
54
56
  // @evidence contracts/portability.md#os-neutral-implementation Mapper processes start through spawnContentMapper's os/exec boundary in each mapper package's directory; the supplied FS owns every file read.
55
- // @evidenceExclude contracts/performance.md#efficient-algorithms The type stores two handles and chooses no algorithm.
57
+ // @evidenceExclude contracts/performance.md#efficient-algorithms This declaration chooses no algorithm; its fields retain shared mapper state and resolved manifest names.
56
58
  // @evidence contracts/performance.md#reuse-equivalent-work Every host built from one ProjectHosts shares one mapper project, so an incremental generation reuses the processes the first generation started.
57
59
  // @evidence contracts/performance.md#bound-retention-and-release-resources The mapper project and host, and with them every spawned mapper process, live until Close; Close releases them once.
58
60
  type ProjectHosts struct {
@@ -60,6 +62,7 @@ type ProjectHosts struct {
60
62
  project contentmapper.Project
61
63
  closeOnce sync.Once
62
64
  closeErr error
65
+ manifests []string
63
66
  }
64
67
 
65
68
  // NewProjectHosts returns the hosts of Programs built from config. It starts
@@ -69,8 +72,8 @@ type ProjectHosts struct {
69
72
  // @evidence contracts/common.md#clear-and-simple-design One gate returns either a nil value or a value holding the mapper project.
70
73
  // @evidence contracts/common.md#prohibited-implementation-shortcuts Mapper resolution and its diagnostics stay with the config parser; this function adds none.
71
74
  // @evidence contracts/common.md#meaningful-documentation Native prose states that construction starts no process.
72
- // @evidence contracts/portability.md#os-neutral-implementation Construction performs no native operation; mapper processes start later through spawnContentMapper.
73
- // @evidenceExclude contracts/performance.md#efficient-algorithms Construction checks one option and one slice length.
75
+ // @evidence contracts/portability.md#os-neutral-implementation Native filepath.Join constructs manifest paths in resolved package directories; mapper processes start later through spawnContentMapper.
76
+ // @evidence contracts/performance.md#efficient-algorithms Construction checks the gate and copies each configured mapper package directory into a manifest path. Cost and returned path storage follow mapper count and path text.
74
77
  // @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call opens its own mapper state; callers share it by holding the value.
75
78
  // @evidence contracts/performance.md#bound-retention-and-release-resources The returned value owns the mapper host and project until Close.
76
79
  func NewProjectHosts(config *innertsoptions.ParsedCommandLine) *ProjectHosts {
@@ -79,8 +82,15 @@ func NewProjectHosts(config *innertsoptions.ParsedCommandLine) *ProjectHosts {
79
82
  }
80
83
  diagnosticLocale, _ := innerlocale.Parse(config.CompilerOptions().Locale)
81
84
  mappers := contentmapper.NewHost(context.Background(), contentmapper.SpawnerFunc(spawnContentMapper), diagnosticLocale)
85
+ manifests := make([]string, 0, len(config.ContentMappers()))
86
+ for _, mapper := range config.ContentMappers() {
87
+ if mapper.PackageDirectory != "" {
88
+ manifests = append(manifests, filepath.Join(string(mapper.PackageDirectory), "package.json"))
89
+ }
90
+ }
82
91
  return &ProjectHosts{
83
- mappers: mappers,
92
+ manifests: manifests,
93
+ mappers: mappers,
84
94
  project: mappers.Project(contentmapper.ProjectSpec{
85
95
  ConfigFileName: config.ConfigName(),
86
96
  Mappers: config.ContentMappers(),
@@ -89,6 +99,31 @@ func NewProjectHosts(config *innertsoptions.ParsedCommandLine) *ProjectHosts {
89
99
  }
90
100
  }
91
101
 
102
+ // Dependencies returns mapper manifests and upstream-declared dynamic inputs.
103
+ // WatchedFiles can open dynamic project configuration; its error is returned
104
+ // alongside manifests so callers keep recovery inputs without claiming success.
105
+ //
106
+ // @evidence contracts/common.md#principled-implementation Uses the pinned upstream Project.WatchedFiles declaration and resolved mapper package directories; it does not infer reads from mapper code.
107
+ // @evidence contracts/common.md#clear-and-simple-design One projection transfers the project-owned dependency population without changing the mapper protocol.
108
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts Upstream errors remain errors and manifests remain available for recovery; no post-compile hash is invented.
109
+ // @evidence contracts/common.md#meaningful-documentation Native prose documents lazy opening, declaration scope and partial error results.
110
+ // @evidence contracts/portability.md#os-neutral-implementation Native filepath.Join anchors manifests in resolved package directories; upstream watched paths retain their absolute native spelling.
111
+ // @evidence contracts/performance.md#efficient-algorithms Copies declared input names and sorts them; upstream WatchedFiles can open dynamic mapper projects and perform protocol IO.
112
+ // @evidence contracts/performance.md#reuse-equivalent-work Upstream project state shares opened configuration; each returned slice is independently owned.
113
+ // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The returned names escape to the caller; ProjectHosts still owns mapper lifetime through Close.
114
+ func (h *ProjectHosts) Dependencies() ([]string, error) {
115
+ if h == nil {
116
+ return nil, nil
117
+ }
118
+ inputs := append([]string(nil), h.manifests...)
119
+ watched, err := h.project.WatchedFiles()
120
+ for _, file := range watched {
121
+ inputs = append(inputs, string(file))
122
+ }
123
+ sort.Strings(inputs)
124
+ return inputs, err
125
+ }
126
+
92
127
  // Host returns a compiler host over fs, like NewHost, that transforms
93
128
  // content-mapped files through this project's mappers.
94
129
  //
@@ -3,6 +3,7 @@
3
3
  package compiler
4
4
 
5
5
  import (
6
+ "slices"
6
7
  "time"
7
8
 
8
9
  "github.com/microsoft/TypeScript/tsc/internal/ast"
@@ -84,8 +85,11 @@ type ProgramResolutionTask struct {
84
85
  redirectedReference module.ResolvedProjectReference
85
86
  }
86
87
 
87
- // ProgramResolutionTasks returns every cached module and type-reference
88
- // resolution, including unresolved entries and automatic type directives.
88
+ // ProgramResolutionTasks returns cached filesystem module and type-reference
89
+ // resolutions, including unresolved entries and automatic type directives.
90
+ // Source-local ambient module sentinels are excluded because the compiler
91
+ // resolves them from source content without a filesystem query. Imports of
92
+ // the same name from other sources and external-module augmentations remain.
89
93
  // A nil Program returns nil. Returned tasks retain Program option and redirect
90
94
  // pointers; the caller owns their storage and must preserve that context.
91
95
  // Enumeration order follows compiler maps and is not sorted. TargetFile is
@@ -93,12 +97,12 @@ type ProgramResolutionTask struct {
93
97
  // initialize referenced configs' input/output maps and query a native realpath
94
98
  // for a preserved symlink redirect; it does not replay module resolution.
95
99
  //
96
- // @evidence contracts/common.md#principled-implementation Enumerating the resident compiler's module and type-directive caches retains both successes and failures; containing paths recover loaded-source or semantic-config spelling, and expected results include package, target and link identity.
100
+ // @evidence contracts/common.md#principled-implementation Enumerating the resident compiler's module and type-directive caches retains filesystem successes and failures while excluding names in the containing source's AmbientModuleNames, matching fileLoader's local-ambient branch. Their declarations remain source-content inputs; other sources and external-module augmentations retain their filesystem tasks. Containing paths recover loaded-source or semantic-config spelling, and expected results include package, target and link identity.
97
101
  // @evidence contracts/common.md#clear-and-simple-design One append closure shares task construction across both compiler cache traversals, while private helpers own source-redirect and project-reference context reconstruction.
98
- // @evidence contracts/common.md#prohibited-implementation-shortcuts The inferred-types filename is the compiler's automatic-directive context, and recovering its lexical directory addresses actual case-canonical cache keys without relaxing result comparison.
102
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts Source-local AmbientModuleNames membership mirrors the compiler's skipped lookup, not a consumer name or an empty-result filter; genuine unresolved tasks remain. The inferred-types filename is the compiler's automatic-directive context, and recovering its lexical directory addresses actual case-canonical cache keys without relaxing result comparison.
99
103
  // @evidence contracts/common.md#meaningful-documentation Native prose states cache coverage, nil behavior and retained Program-pointer lifetime; task fields document the lexical-versus-loaded path distinctions.
100
104
  // @evidence contracts/portability.md#os-neutral-implementation Compiler canonical cache keys are not replayed as lexical filenames: source/config context restores spelling, and redirects use actual filesystem Realpath plus Program case policy rather than OS guesses.
101
- // @evidence contracts/performance.md#efficient-algorithms Two cache traversals append one task per resolution and share one reference-context snapshot. Additional work includes reference-graph traversal, source/output map scans and first-use output-path construction, path hashing/canonicalization and selected native Realpath queries for symlink context. Map/string backing and referenced config/source population matter beyond just the returned task count; extraction does not independently replay imports.
105
+ // @evidence contracts/performance.md#efficient-algorithms Two cache traversals append one task per filesystem resolution and share one reference-context snapshot. Each module entry looks up its containing source and scans that source's ambient names, matching upstream's membership procedure without another retained index. Additional work includes reference-graph traversal, source/output map scans and first-use output-path construction, path hashing/canonicalization and selected native Realpath queries for symlink context. Map/string backing and referenced config/source population matter beyond just the returned task count; extraction does not independently replay imports.
102
106
  // @evidence contracts/performance.md#reuse-equivalent-work Cached resident resolutions and one shared project-reference context are reused across tasks from the same Program, preserving their option and redirect identity rather than sharing across changed programs.
103
107
  // @evidence contracts/performance.md#bound-retention-and-release-resources The returned task slice retains option, expected-result strings and redirect metadata, plus a shared context's directory set and output-to-reference map when present. These pointers can keep parsed project/config/source graphs reachable beyond the shallow map size. First-use input/output maps remain owned by referenced configs; invocation-local traversal/snapshot containers add no independent historical registry or running task, and callers bound how long they retain the result.
104
108
  func ProgramResolutionTasks(program *Program) []ProgramResolutionTask {
@@ -154,6 +158,11 @@ func ProgramResolutionTasks(program *Program) []ProgramResolutionTask {
154
158
  })
155
159
  }
156
160
  program.ForEachResolvedModule(func(resolution *module.ResolvedModule, name string, mode core.ResolutionMode, filePath tspath.PathKey) {
161
+ // fileLoader records local ambient names as empty cache sentinels without
162
+ // asking the filesystem resolver. Their source content owns the proof.
163
+ if source := program.GetSourceFileByPath(filePath); source != nil && slices.Contains(source.AmbientModuleNames, name) {
164
+ return
165
+ }
157
166
  appendTask(ProgramResolutionKindModule, name, mode, filePath, moduleResolutionResult(resolution), resolution)
158
167
  }, nil)
159
168
  program.ForEachResolvedTypeReferenceDirective(func(resolution *module.ResolvedTypeReferenceDirective, name string, mode core.ResolutionMode, filePath tspath.PathKey) {
package/utility/host.go CHANGED
@@ -48,6 +48,9 @@ type hostOptions struct {
48
48
  // host sets it (to an OverlayFS), so build/check/transform leave it nil and
49
49
  // LoadProgram falls back to the default filesystem.
50
50
  fs vfs.FS
51
+
52
+ // failureInputs receives the loader's actual pre-Program recovery graph.
53
+ failureInputs func(*driver.TransformGraph)
51
54
  }
52
55
 
53
56
  // transformResult is the JSON envelope written to stdout by RunTransform.
@@ -115,8 +118,8 @@ func RunCheck(args []string) int {
115
118
  //
116
119
  // After successful flag admission, requested metadata is attempted on both
117
120
  // successful and failed checks and belongs to that check generation. A missing
118
- // generation is explicitly incomplete; rejected flag admission installs no
119
- // publication defer. Input authority does not erase status or diagnostics.
121
+ // generation is explicitly incomplete but retains observed load-recovery paths.
122
+ // Rejected flag admission installs no publication defer. Input authority does not erase status or diagnostics.
120
123
  // Callers own the metadata artifact's removal and supplied-stream lifetime.
121
124
  // Diagnostic/report writes are best-effort and are not delivery receipts.
122
125
  //
@@ -135,9 +138,13 @@ func RunCheckWithIO(args []string, stdout, stderr io.Writer) (status int) {
135
138
  }
136
139
  opts.noEmit = true
137
140
  var prog *driver.Program
141
+ var recovery *driver.TransformGraph
142
+ if opts.checkObservationsJSON != "" {
143
+ opts.failureInputs = func(graph *driver.TransformGraph) { recovery = graph }
144
+ }
138
145
  defer func() {
139
146
  if opts.checkObservationsJSON != "" {
140
- if err := driver.WriteCheckObservationsJSON(opts.checkObservationsJSON, prog); err != nil {
147
+ if err := driver.WriteCheckRecoveryObservationsJSON(opts.checkObservationsJSON, prog, recovery); err != nil {
141
148
  fmt.Fprintf(opts.stderr, "ttsc utility: check observations write failed: %v\n", err)
142
149
  if status == 0 {
143
150
  status = 3
@@ -307,8 +314,9 @@ func RunTransform(args []string) int {
307
314
  // Failed projects retain diagnostics and recovery inputs but publish no partial
308
315
  // TypeScript. Encoding or response-write failure returns 3; project failure
309
316
  // returns 2.
310
- // Recovery envelopes require a loaded generation; flag admission or load failure
311
- // returns before response construction. A loaded generation's graph is captured
317
+ // Rejected flag admission or a loader error returns before response construction.
318
+ // Config/argument diagnostics can retain observed recovery predicates without a
319
+ // Program. A loaded generation's graph is captured
312
320
  // before linked program hooks mutate its ASTs. Diagnostic stream reporting is
313
321
  // best-effort; response encoding and returned write errors determine delivery
314
322
  // status. Callers own the supplied stream lifetimes.
@@ -326,16 +334,20 @@ func RunTransformWithIO(args []string, stdout, stderr io.Writer) int {
326
334
  if !ok {
327
335
  return 2
328
336
  }
337
+ out := transformResult{TypeScript: map[string]string{}}
338
+ opts.failureInputs = func(graph *driver.TransformGraph) { out.Graph = graph }
329
339
  prog, _, diags, ok := loadUtilityProgramWithDiagnostics(opts)
330
340
  if !ok {
331
341
  return 2
332
342
  }
333
- out := transformResult{TypeScript: map[string]string{}}
334
343
  if prog != nil {
335
344
  defer prog.Close()
336
345
  // The same failed Program owns the missing resolution candidates needed
337
346
  // for recovery. Capture them before closing it or applying mutations.
338
347
  out.Graph = driver.NewTransformGraph(prog, opts.cwd)
348
+ } else {
349
+ unavailable := false
350
+ out.ObservationsComplete = &unavailable
339
351
  }
340
352
  if len(diags) != 0 {
341
353
  driver.WritePrettyDiagnostics(opts.stderr, diags, opts.cwd)
@@ -605,6 +617,7 @@ func loadUtilityProgramWithDiagnostics(opts hostOptions) (*driver.Program, []dri
605
617
  Checkers: opts.checkers,
606
618
  TsgoArgs: opts.tsgoArgs,
607
619
  FS: opts.fs,
620
+ OnFailureInputs: opts.failureInputs,
608
621
  })
609
622
  if err != nil {
610
623
  fmt.Fprintf(opts.stderr, "ttsc utility: %v\n", err)