ttsc 0.34.1 → 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.
- package/cmd/ttsc/api_transform.go +13 -3
- package/cmd/ttsc/api_transform_config_recovery_test.go +44 -0
- package/driver/check_observations.go +24 -2
- package/driver/graph.go +40 -3
- package/driver/input_observation.go +16 -0
- package/driver/program.go +41 -15
- package/driver/recovery_graph.go +25 -0
- package/driver/target_ts70.go +12 -0
- package/driver/target_ts71.go +12 -0
- package/driver/transform_dependencies.go +22 -1
- package/driver/transform_membership.go +61 -0
- package/lib/compiler/internal/build/appendBuildOutput.d.ts +1 -1
- package/lib/compiler/internal/build/appendBuildOutput.js +37 -5
- package/lib/compiler/internal/build/appendBuildOutput.js.map +1 -1
- package/lib/compiler/internal/parseNativeTransformOutput.js +31 -1
- package/lib/compiler/internal/parseNativeTransformOutput.js.map +1 -1
- package/lib/internal/CompilerResultAdapter.d.ts +7 -5
- package/lib/internal/CompilerResultAdapter.js +29 -6
- package/lib/internal/CompilerResultAdapter.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +25 -1
- package/package.json +8 -8
- package/shim/compiler/neutral.go +14 -0
- package/src/compiler/internal/build/appendBuildOutput.ts +42 -5
- package/src/compiler/internal/parseNativeTransformOutput.ts +45 -1
- package/src/internal/CompilerResultAdapter.ts +29 -6
- package/src/structures/ITtscCompilerTransformation.ts +28 -1
- package/targets/7.1/bridge/compiler/neutral.go +39 -4
- package/utility/host.go +19 -6
|
@@ -69,13 +69,16 @@ type apiTransformResult struct {
|
|
|
69
69
|
// The actual preparation supplies ForceNoEmit and resolved argv/cwd. This
|
|
70
70
|
// wrapper owns its one LoadProgram call and closes a nonnil acquired Program
|
|
71
71
|
// after the borrowed source-response operation returns. That response owner
|
|
72
|
-
// constructs the graph before linked source hooks and never emits files.
|
|
72
|
+
// constructs the graph before linked source hooks and never emits files. Failed
|
|
73
|
+
// config/argument parsing retains its actual recovery graph without a Program.
|
|
73
74
|
func runAPITransform(args []string) int {
|
|
74
75
|
request, code := prepareAPITransformInvocation(args)
|
|
75
76
|
if code != 0 {
|
|
76
77
|
return code
|
|
77
78
|
}
|
|
78
79
|
cwd := request.cwd
|
|
80
|
+
var recovery *driver.TransformGraph
|
|
81
|
+
request.options.OnFailureInputs = func(graph *driver.TransformGraph) { recovery = graph }
|
|
79
82
|
prog, diags, err := driver.LoadProgram(cwd, request.tsconfigPath, request.options)
|
|
80
83
|
if err != nil {
|
|
81
84
|
fmt.Fprintf(stderr, "ttsc api-transform: %v\n", err)
|
|
@@ -84,7 +87,7 @@ func runAPITransform(args []string) int {
|
|
|
84
87
|
if prog != nil {
|
|
85
88
|
defer prog.Close()
|
|
86
89
|
}
|
|
87
|
-
return writeTransformedProgramResponse(prog, diags, cwd)
|
|
90
|
+
return writeTransformedProgramResponse(prog, diags, cwd, recovery)
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
// writeTransformedProgramResponse borrows the loaded generation for the actual
|
|
@@ -102,14 +105,21 @@ func runAPITransform(args []string) int {
|
|
|
102
105
|
// Performance: Efficient algorithms: The original graph, resident-file enumeration and one JSON encoding remain; work scales with actual graph/source/envelope bytes.
|
|
103
106
|
// Performance: Reuse equivalent work: An immutable no-plugin generation already emitted to memory can supply its original source text and graph without a second load. This is operation-level reuse, not a claim of equal compile/transform loader policies.
|
|
104
107
|
// Performance: Bound retention and release resources: The caller retains the checker lease across borrowing and closes it once after the final consumer; this operation creates invocation-local envelope collections and does not own the lease or a retained cache.
|
|
105
|
-
func writeTransformedProgramResponse(prog *driver.Program, diags []driver.Diagnostic, cwd string) int {
|
|
108
|
+
func writeTransformedProgramResponse(prog *driver.Program, diags []driver.Diagnostic, cwd string, recovery ...*driver.TransformGraph) int {
|
|
106
109
|
typescript := map[string]string{}
|
|
107
110
|
var dependencies driver.TransformDependencies
|
|
108
111
|
var graph *driver.TransformGraph
|
|
112
|
+
if len(recovery) != 0 {
|
|
113
|
+
graph = recovery[0]
|
|
114
|
+
}
|
|
109
115
|
var hostInputs []string
|
|
110
116
|
var hostInputHashes map[string]*string
|
|
111
117
|
var hostInputRealpaths map[string]*string
|
|
112
118
|
var observationsComplete *bool
|
|
119
|
+
if prog == nil {
|
|
120
|
+
unavailable := false
|
|
121
|
+
observationsComplete = &unavailable
|
|
122
|
+
}
|
|
113
123
|
if prog != nil {
|
|
114
124
|
// Compute the reference graph before SourceFiles() runs linked plugin
|
|
115
125
|
// hooks: those mutate parsed ASTs in place, and the graph must describe
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"slices"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestAPITransformConfigFailureRetainsRecoveryInputs verifies the actual
|
|
10
|
+
// built-in transform command transports failed-load observations.
|
|
11
|
+
//
|
|
12
|
+
// Source output cannot carry repair inputs when config parsing creates no Program.
|
|
13
|
+
//
|
|
14
|
+
// 1. Run the actual API command on a missing extended config and response file.
|
|
15
|
+
// 2. Decode its native stdout envelope and retain the failure status.
|
|
16
|
+
// 3. Require missing config predicates and response reads without source output.
|
|
17
|
+
//
|
|
18
|
+
// @evidence contracts/testing.md#behavioral-verification Real runAPITransform consumes its ordinary argument preparation and loader, then serializes failed-load recovery metadata rather than dropping it with the nil Program.
|
|
19
|
+
// @evidence contracts/testing.md#independent-expectations Status 2, empty TypeScript, literal missing.json/main.rsp inputs and a false existence predicate define expected failure transport independently of command output.
|
|
20
|
+
// @evidence contracts/testing.md#distinguishing-cases A pre-Program config failure contrasts the existing semantic-error Program case; actual argv response expansion remains observable in the failure graph on both targets.
|
|
21
|
+
// @evidence contracts/testing.md#execution-ownership The command Go runner owns one temporary project and invocation-local captured streams; the actual command executes in process without a separate build or compiler subprocess.
|
|
22
|
+
func TestAPITransformConfigFailureRetainsRecoveryInputs(t *testing.T) {
|
|
23
|
+
root := t.TempDir()
|
|
24
|
+
writeCommandProjectFile(t, root, "tsconfig.json", `{"extends":"./missing.json","files":["main.ts"]}`)
|
|
25
|
+
writeCommandProjectFile(t, root, "main.ts", "export const value = 1;\n")
|
|
26
|
+
writeCommandProjectFile(t, root, "main.rsp", "--strict\n")
|
|
27
|
+
code, output, stderr := captureCommand(t, func() int {
|
|
28
|
+
return runAPITransform([]string{"--cwd", root, "--tsconfig", "tsconfig.json", "--tsgo-args", `["@main.rsp"]`})
|
|
29
|
+
})
|
|
30
|
+
var result apiTransformResult
|
|
31
|
+
if err := json.Unmarshal([]byte(output), &result); err != nil {
|
|
32
|
+
t.Fatalf("response decode: %v stdout=%s stderr=%s", err, output, stderr)
|
|
33
|
+
}
|
|
34
|
+
if code != 2 || len(result.TypeScript) != 0 || result.Graph == nil || !slices.Contains(result.Graph.ResolutionInputs, "missing.json") || !slices.Contains(result.Graph.ResolutionInputs, "main.rsp") {
|
|
35
|
+
t.Fatalf("lost config recovery metadata: code=%d output=%s stderr=%s", code, output, stderr)
|
|
36
|
+
}
|
|
37
|
+
if result.ObservationsComplete == nil || *result.ObservationsComplete {
|
|
38
|
+
t.Fatalf("failed load claimed complete observations: %s", output)
|
|
39
|
+
}
|
|
40
|
+
absent := result.Graph.InputObservations["missing.json"].FileExists
|
|
41
|
+
if absent == nil || *absent {
|
|
42
|
+
t.Fatalf("lost config absence predicate: %s", output)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -11,8 +11,10 @@ import (
|
|
|
11
11
|
// caller must keep the Program open until this snapshot is written. Command
|
|
12
12
|
// status and diagnostics remain independent of these observation facts.
|
|
13
13
|
//
|
|
14
|
-
// A nil Program publishes empty observation collections and an explicit
|
|
15
|
-
// marker
|
|
14
|
+
// A nil Program publishes empty hook observation collections and an explicit
|
|
15
|
+
// incomplete marker. WriteCheckRecoveryObservationsJSON additionally accepts
|
|
16
|
+
// actual failed-load CLI/config predicates without completed Program authority.
|
|
17
|
+
// For a loaded Program,
|
|
16
18
|
// only an explicit hook limitation adds that marker. Its absence does not prove
|
|
17
19
|
// the population complete or repair missing or conflicting individual proofs.
|
|
18
20
|
// The caller owns a fresh result path, its parent directory and eventual artifact
|
|
@@ -27,6 +29,23 @@ import (
|
|
|
27
29
|
// @evidence contracts/performance.md#reuse-equivalent-work The same check generation's recorded observations supply the result without reparsing sources or reexecuting configuration hooks.
|
|
28
30
|
// @evidence contracts/performance.md#bound-retention-and-release-resources Scope snapshots and encoding buffers are call-local without a payload cap. The shared publisher closes its temporary descriptor before rename and attempts removal on failure, returning cleanup errors; the caller owns the Program, fresh destination, parent directory and final artifact removal.
|
|
29
31
|
func WriteCheckObservationsJSON(fileName string, program *Program) error {
|
|
32
|
+
return WriteCheckRecoveryObservationsJSON(fileName, program, nil)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// WriteCheckRecoveryObservationsJSON publishes check observations with an
|
|
36
|
+
// optional failed-load recovery graph. A loaded Program always supplies its own
|
|
37
|
+
// graph instead; nil Program remains explicitly incomplete. The recovery graph
|
|
38
|
+
// comes from that invocation's OnFailureInputs sink, not a later filesystem read.
|
|
39
|
+
//
|
|
40
|
+
// @evidence contracts/common.md#principled-implementation Loaded Program observations retain their generation; only a nil Program admits supplied failed-load predicates, with explicit incomplete hook authority.
|
|
41
|
+
// @evidence contracts/common.md#clear-and-simple-design One publisher serves the original two-argument API and native callers retaining failed-load recovery inputs.
|
|
42
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts No fake Program, reconstructed resolver or later read supplies recovery metadata; status remains separate.
|
|
43
|
+
// @evidence contracts/common.md#meaningful-documentation Native prose states recovery provenance, Program precedence and incomplete authority.
|
|
44
|
+
// @evidence contracts/portability.md#os-neutral-implementation Native absolute path validation and the existing same-directory private publisher retain observation spellings unchanged.
|
|
45
|
+
// @evidence contracts/performance.md#efficient-algorithms Program accessors snapshot current ledgers and graph; JSON encoding and publication visit retained predicate/path bytes and allocate the serialized payload without a population cap.
|
|
46
|
+
// @evidence contracts/performance.md#reuse-equivalent-work The invocation's existing observations supply metadata without another compiler load or filesystem proof read.
|
|
47
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The caller owns Program/recovery lifetimes and the final artifact; call-local snapshots and encoding buffers add no historical registry, and the shared publisher closes temporary handles.
|
|
48
|
+
func WriteCheckRecoveryObservationsJSON(fileName string, program *Program, recovery *TransformGraph) error {
|
|
30
49
|
if !filepath.IsAbs(fileName) {
|
|
31
50
|
return fmt.Errorf("driver: check observations path must be absolute: %q", fileName)
|
|
32
51
|
}
|
|
@@ -35,6 +54,9 @@ func WriteCheckObservationsJSON(fileName string, program *Program) error {
|
|
|
35
54
|
realpaths := map[string]*string{}
|
|
36
55
|
var complete *bool
|
|
37
56
|
var graph *TransformGraph
|
|
57
|
+
if program == nil {
|
|
58
|
+
graph = recovery
|
|
59
|
+
}
|
|
38
60
|
if program == nil || program.PluginObservationsIncomplete() {
|
|
39
61
|
unavailable := false
|
|
40
62
|
complete = &unavailable
|
package/driver/graph.go
CHANGED
|
@@ -37,7 +37,7 @@ const bundledScheme = "bundled:///"
|
|
|
37
37
|
// results. They are a separate class from resolved edges: a predicate or
|
|
38
38
|
// package manifest changing can change an unchanged reference's meaning.
|
|
39
39
|
// - ResolutionInputs lists automatic type discovery and resolution inputs
|
|
40
|
-
// whose state can affect every source file, including type-root directory
|
|
40
|
+
// whose state can affect every source file, including compiler response files and type-root directory
|
|
41
41
|
// membership.
|
|
42
42
|
// - InputObservations preserves the independent filesystem predicates the
|
|
43
43
|
// compiler actually asked. InputHashes and InputRealpaths retain the legacy
|
|
@@ -48,6 +48,14 @@ const bundledScheme = "bundled:///"
|
|
|
48
48
|
// project's root specs and compared paths with, so a host deciding the
|
|
49
49
|
// same membership uses the compiler's policy rather than a guess from the
|
|
50
50
|
// platform.
|
|
51
|
+
// - ContentMapperInputs lists mapper manifests and upstream-declared dynamic
|
|
52
|
+
// dependencies for every output. They are declarations, not observed reads,
|
|
53
|
+
// and require independent before/after dependency witnesses.
|
|
54
|
+
//
|
|
55
|
+
// A failed-load recovery graph contains only the CLI/config predicates observed
|
|
56
|
+
// before Program construction failed. Empty source/config lists and absent
|
|
57
|
+
// membership settings supply no completed Program authority. Failure status and
|
|
58
|
+
// explicit observation limitations remain in the containing response.
|
|
51
59
|
//
|
|
52
60
|
// Keys and values use the same convention as the envelope's `typescript`
|
|
53
61
|
// map: project-relative slash paths, falling back to slash-normalized
|
|
@@ -62,6 +70,11 @@ const bundledScheme = "bundled:///"
|
|
|
62
70
|
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This schema carries proof without coordinating artifact reuse.
|
|
63
71
|
// @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The caller-owned value owns no resident cache or resource.
|
|
64
72
|
type TransformGraph struct {
|
|
73
|
+
// Membership is the effective native root-admission and output-exclusion policy.
|
|
74
|
+
Membership *TransformMembership `json:"membership,omitempty"`
|
|
75
|
+
// ContentMapperInputs are declared project dependencies, not observed reads.
|
|
76
|
+
// Consumers witness them before compilation independently of graph proof.
|
|
77
|
+
ContentMapperInputs []string `json:"contentMapperInputs,omitempty"`
|
|
65
78
|
Edges map[string][]string `json:"edges"`
|
|
66
79
|
Globals []string `json:"globals"`
|
|
67
80
|
Configs []string `json:"configs"`
|
|
@@ -94,6 +107,7 @@ func NewTransformGraph(prog *Program, cwd string) *TransformGraph {
|
|
|
94
107
|
}
|
|
95
108
|
resolution := ObserveProgramResolutions(prog, cwd)
|
|
96
109
|
graph := &TransformGraph{
|
|
110
|
+
Membership: programRootMembership(prog),
|
|
97
111
|
Edges: map[string][]string{},
|
|
98
112
|
Globals: []string{},
|
|
99
113
|
Configs: []string{},
|
|
@@ -101,6 +115,22 @@ func NewTransformGraph(prog *Program, cwd string) *TransformGraph {
|
|
|
101
115
|
ResolutionInputs: resolution.Universal,
|
|
102
116
|
UseCaseSensitiveFileNames: prog.TSProgram.UseCaseSensitiveFileNames(),
|
|
103
117
|
}
|
|
118
|
+
for _, input := range prog.mapperDependencies {
|
|
119
|
+
graph.ContentMapperInputs = append(graph.ContentMapperInputs, TransformOutputKey(cwd, input))
|
|
120
|
+
}
|
|
121
|
+
sort.Strings(graph.ContentMapperInputs)
|
|
122
|
+
argumentInputs := map[string]struct{}{}
|
|
123
|
+
for _, input := range graph.ResolutionInputs {
|
|
124
|
+
argumentInputs[input] = struct{}{}
|
|
125
|
+
}
|
|
126
|
+
for _, input := range prog.compilerArgumentInputs {
|
|
127
|
+
key := TransformOutputKey(cwd, input)
|
|
128
|
+
if _, found := argumentInputs[key]; !found {
|
|
129
|
+
graph.ResolutionInputs = append(graph.ResolutionInputs, key)
|
|
130
|
+
argumentInputs[key] = struct{}{}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
sort.Strings(graph.ResolutionInputs)
|
|
104
134
|
for _, file := range prog.TSProgram.SourceFiles() {
|
|
105
135
|
fileName := file.FileName()
|
|
106
136
|
if strings.HasPrefix(string(fileName), bundledScheme) {
|
|
@@ -135,6 +165,13 @@ func (graph *TransformGraph) attachInputProof(prog *Program, cwd string) {
|
|
|
135
165
|
if prog == nil || prog.inputObserver == nil {
|
|
136
166
|
return
|
|
137
167
|
}
|
|
168
|
+
graph.attachObservedInputProof(prog.inputObserver, cwd)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// attachObservedInputProof projects only this observer's existing predicates.
|
|
172
|
+
// Failed CLI/config loads use it without constructing a compiler Program;
|
|
173
|
+
// successful graph construction retains the same realized-source requirements.
|
|
174
|
+
func (graph *TransformGraph) attachObservedInputProof(observer *inputObservationFS, cwd string) {
|
|
138
175
|
inputs := map[string]struct{}{}
|
|
139
176
|
realized := map[string]struct{}{}
|
|
140
177
|
addRealized := func(input string) {
|
|
@@ -174,14 +211,14 @@ func (graph *TransformGraph) attachInputProof(prog *Program, cwd string) {
|
|
|
174
211
|
if !filepath.IsAbs(file) {
|
|
175
212
|
file = filepath.Join(cwd, file)
|
|
176
213
|
}
|
|
177
|
-
observation, predicateFailure :=
|
|
214
|
+
observation, predicateFailure := observer.predicateProof(file)
|
|
178
215
|
if predicateFailure == "" {
|
|
179
216
|
observations[input] = observation
|
|
180
217
|
}
|
|
181
218
|
var hash, realpath *string
|
|
182
219
|
legacyFailure := predicateFailure
|
|
183
220
|
if predicateFailure == "" {
|
|
184
|
-
hash, realpath, legacyFailure =
|
|
221
|
+
hash, realpath, legacyFailure = observer.proof(file)
|
|
185
222
|
}
|
|
186
223
|
if legacyFailure != "" {
|
|
187
224
|
// A resolver input can have a complete predicate proof that the legacy
|
|
@@ -271,6 +271,22 @@ func boolPointer(value bool) *bool {
|
|
|
271
271
|
return &value
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
// readPaths snapshots the lexical reads already made through this observer.
|
|
275
|
+
// LoadProgram calls it immediately after upstream argument parsing, before
|
|
276
|
+
// configuration or source reads can enter the population. It performs no new
|
|
277
|
+
// filesystem query and keeps failed/conflicting observations for graph proof.
|
|
278
|
+
func (fs *inputObservationFS) readPaths() []string {
|
|
279
|
+
fs.mu.Lock()
|
|
280
|
+
defer fs.mu.Unlock()
|
|
281
|
+
paths := make([]string, 0, len(fs.observationOrder))
|
|
282
|
+
for _, key := range fs.observationOrder {
|
|
283
|
+
if fs.observations[key].proof.ReadFile != nil {
|
|
284
|
+
paths = append(paths, fs.observationSpellings[key])
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return paths
|
|
288
|
+
}
|
|
289
|
+
|
|
274
290
|
func (fs *inputObservationFS) observationKey(path string) string {
|
|
275
291
|
if !filepath.IsAbs(path) {
|
|
276
292
|
return ""
|
package/driver/program.go
CHANGED
|
@@ -323,7 +323,10 @@ type Program struct {
|
|
|
323
323
|
Host shimcompiler.CompilerHost
|
|
324
324
|
// hosts builds every generation's Host and owns the content-mapper processes
|
|
325
325
|
// those hosts share; Close releases it.
|
|
326
|
-
hosts
|
|
326
|
+
hosts *shimcompiler.ProjectHosts
|
|
327
|
+
mapperDependencies []string
|
|
328
|
+
mapperDependencyError error
|
|
329
|
+
compilerArgumentInputs []string
|
|
327
330
|
|
|
328
331
|
// FS is the input-observing filesystem, including any source preamble layer.
|
|
329
332
|
FS vfs.FS
|
|
@@ -340,10 +343,10 @@ type Program struct {
|
|
|
340
343
|
// the program. `ForceEmit` is used by `ttsc --emit` and runtime compilation
|
|
341
344
|
// so execution still works when the project defaults to `noEmit`.
|
|
342
345
|
//
|
|
343
|
-
// @evidence contracts/common.md#principled-implementation Explicit overrides
|
|
346
|
+
// @evidence contracts/common.md#principled-implementation Explicit overrides, an optional FS and a failure-observation sink represent caller-owned load choices separately from resolved tsconfig values.
|
|
344
347
|
// @evidence contracts/common.md#clear-and-simple-design One options value carries load-time choices; compiler option merging stays with LoadProgram and the native parser.
|
|
345
348
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts Dedicated fields express supported overrides without deriving policy from a fixture or consumer name.
|
|
346
|
-
// @evidence contracts/common.md#meaningful-documentation Native member prose explains force emit, semantic config ownership, threading
|
|
349
|
+
// @evidence contracts/common.md#meaningful-documentation Native member prose explains force emit, semantic config ownership, threading, optional FS and failed-load observation ownership under documentation-skill guidance.
|
|
347
350
|
// @evidence contracts/portability.md#os-neutral-implementation FS supplies native capabilities and path fields retain caller spellings for the loader's supported normalization boundary.
|
|
348
351
|
// @evidenceExclude contracts/performance.md#efficient-algorithms Options encode choices but perform no compilation algorithm.
|
|
349
352
|
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This input value does not establish whether loaded work can be shared.
|
|
@@ -394,6 +397,12 @@ type LoadProgramOptions struct {
|
|
|
394
397
|
// is used. A resident Session passes an overlay FS so in-memory edits stay
|
|
395
398
|
// visible to the program and to incremental UpdateProgram calls.
|
|
396
399
|
FS vfs.FS
|
|
400
|
+
|
|
401
|
+
// OnFailureInputs receives the actual CLI/config input observations if this
|
|
402
|
+
// load exits after observation starts but before a Program is acquired. It
|
|
403
|
+
// runs synchronously once; it does not report a completed source population.
|
|
404
|
+
// Successful loads expose their graph through NewTransformGraph instead.
|
|
405
|
+
OnFailureInputs func(*TransformGraph)
|
|
397
406
|
}
|
|
398
407
|
|
|
399
408
|
// Close releases the checker pool lease acquired by LoadProgram and, on the
|
|
@@ -582,13 +591,13 @@ func forceSingleChecker(parsed *tsoptions.ParsedCommandLine) {
|
|
|
582
591
|
// callers should provide an absolute anchor. tsconfigPath may be relative to it.
|
|
583
592
|
//
|
|
584
593
|
// @evidence contracts/common.md#principled-implementation Plugin preambles enter before parsing, native CLI/config merging resolves options before Program creation, and the facade associates the observing VFS and borrowed checker with that generation; this is not a certificate of every filesystem access by plugins or their host.
|
|
585
|
-
// @evidence contracts/common.md#clear-and-simple-design One loader orders plugin config, filesystem layers, config overrides and checker acquisition;
|
|
594
|
+
// @evidence contracts/common.md#clear-and-simple-design One loader orders plugin config, filesystem layers, config overrides and checker acquisition; an optional failure sink transfers only the rejected load's observed recovery inputs.
|
|
586
595
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts Invalid configuration or CLI input returns before construction; an explicitly empty argument list does not fall through to an ancestor's flags.
|
|
587
596
|
// @evidence contracts/common.md#meaningful-documentation Native prose and options document one-shot load, path anchoring, force overrides and checker ownership under documentation-skill guidance.
|
|
588
597
|
// @evidence contracts/portability.md#os-neutral-implementation Native cwd resolution uses filepath/tspath and the chosen VFS provides actual case and path capabilities; semantic wrapper ownership uses an explicit config path.
|
|
589
|
-
// @evidence contracts/performance.md#efficient-algorithms One load composes plugin/preamble work, config/CLI parsing, native Program processing and checker acquisition with VFS observation. Read-text hashing runs on each observed read, including repeated reads; config/source bytes, native resolution and directory work, plugin callbacks and retained observation maps govern cost rather than only facade construction.
|
|
598
|
+
// @evidence contracts/performance.md#efficient-algorithms One load composes plugin/preamble work, config/CLI parsing, native Program processing and checker acquisition with VFS observation. A requested failed-load graph additionally snapshots, projects and sorts the observed predicate population and invokes the caller's sink. Read-text hashing runs on each observed read, including repeated reads; config/source bytes, native resolution and directory work, plugin callbacks and retained observation maps govern cost rather than only facade construction.
|
|
590
599
|
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This one-shot factory does not coordinate callers; Session and resident hosts own cross-request compiler reuse.
|
|
591
|
-
// @evidence contracts/performance.md#bound-retention-and-release-resources Errors before checker acquisition return no lease.
|
|
600
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources Errors before checker acquisition return no lease; an optional synchronous sink receives call-owned recovery metadata once after observation starts. Successful loading transfers checker and mapper ownership plus input/plugin ledgers to Program; Close releases owned native resources, while caller reachability controls remaining facade state.
|
|
592
601
|
func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program, []Diagnostic, error) {
|
|
593
602
|
if !filepath.IsAbs(cwd) {
|
|
594
603
|
if abs, err := filepath.Abs(cwd); err == nil {
|
|
@@ -616,6 +625,14 @@ func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program
|
|
|
616
625
|
fs = DefaultFS()
|
|
617
626
|
}
|
|
618
627
|
inputObserver := newInputObservationFS(fs)
|
|
628
|
+
loaded := false
|
|
629
|
+
if options.OnFailureInputs != nil {
|
|
630
|
+
defer func() {
|
|
631
|
+
if !loaded {
|
|
632
|
+
options.OnFailureInputs(inputObserver.recoveryGraph(cwd))
|
|
633
|
+
}
|
|
634
|
+
}()
|
|
635
|
+
}
|
|
619
636
|
fs = inputObserver
|
|
620
637
|
if options.SourcePreamble != "" {
|
|
621
638
|
fs = sourcePreambleFS{
|
|
@@ -646,6 +663,7 @@ func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program
|
|
|
646
663
|
if len(cliDiags) > 0 {
|
|
647
664
|
return nil, cliDiags, nil
|
|
648
665
|
}
|
|
666
|
+
compilerArgumentInputs := inputObserver.readPaths()
|
|
649
667
|
|
|
650
668
|
// Config identity is explicit; argument conversion uses the original argv
|
|
651
669
|
// base, while the Program and linked plugin state retain their selected cwd.
|
|
@@ -684,21 +702,26 @@ func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program
|
|
|
684
702
|
hosts := shimcompiler.NewProjectHosts(parsed)
|
|
685
703
|
host = projectHost(hosts, cwd, fs)
|
|
686
704
|
tsProgram, _, _ := CreateProgramFromConfig(parsed, host)
|
|
705
|
+
mapperDependencies, mapperDependencyError := hosts.Dependencies()
|
|
687
706
|
|
|
688
707
|
checker, done := tsProgram.GetTypeChecker(context.Background())
|
|
689
708
|
prog := &Program{
|
|
690
|
-
TSProgram:
|
|
691
|
-
ParsedConfig:
|
|
692
|
-
Checker:
|
|
693
|
-
checkerRelease:
|
|
694
|
-
Host:
|
|
695
|
-
hosts:
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
709
|
+
TSProgram: tsProgram,
|
|
710
|
+
ParsedConfig: parsed,
|
|
711
|
+
Checker: checker,
|
|
712
|
+
checkerRelease: done,
|
|
713
|
+
Host: host,
|
|
714
|
+
hosts: hosts,
|
|
715
|
+
mapperDependencies: mapperDependencies,
|
|
716
|
+
mapperDependencyError: mapperDependencyError,
|
|
717
|
+
compilerArgumentInputs: compilerArgumentInputs,
|
|
718
|
+
FS: fs,
|
|
719
|
+
inputObserver: inputObserver,
|
|
720
|
+
SourcePreamble: options.SourcePreamble,
|
|
699
721
|
}
|
|
700
722
|
prog.plugins = pluginState
|
|
701
723
|
e2etrace.Program("program-load-outcome", "driver-create", "facade-installed", false, tsProgram)
|
|
724
|
+
loaded = true
|
|
702
725
|
return prog, nil, nil
|
|
703
726
|
}
|
|
704
727
|
|
|
@@ -1101,6 +1124,9 @@ func (p *Program) diagnostics(files []*ast.SourceFile) []Diagnostic {
|
|
|
1101
1124
|
// `driver: nil program` above is the precedent for a driver-level entry with
|
|
1102
1125
|
// no file or code.
|
|
1103
1126
|
var out []Diagnostic
|
|
1127
|
+
if p.mapperDependencyError != nil {
|
|
1128
|
+
out = append(out, Diagnostic{Severity: SeverityError, Message: "driver: content mapper dependencies: " + p.mapperDependencyError.Error()})
|
|
1129
|
+
}
|
|
1104
1130
|
if p.pluginsApplied && p.pluginsApplyErr != nil {
|
|
1105
1131
|
out = append(out, Diagnostic{
|
|
1106
1132
|
Severity: SeverityError,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package driver
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"sort"
|
|
5
|
+
|
|
6
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// recoveryGraph retains the predicates already observed before a failed load.
|
|
10
|
+
// Empty source/config populations make no Program-membership claim; every
|
|
11
|
+
// observed path is universal recovery input, including missing candidates.
|
|
12
|
+
// Snapshotting and proof projection perform no new filesystem operation. Work
|
|
13
|
+
// and returned storage follow observed paths/predicates plus path sorting.
|
|
14
|
+
func (fs *inputObservationFS) recoveryGraph(cwd string) *TransformGraph {
|
|
15
|
+
graph := &TransformGraph{
|
|
16
|
+
Edges: map[string][]string{}, Globals: []string{}, Configs: []string{},
|
|
17
|
+
UseCaseSensitiveFileNames: fs.caseSensitivity == shimtspath.CaseSensitive,
|
|
18
|
+
}
|
|
19
|
+
for _, input := range fs.observedPaths() {
|
|
20
|
+
graph.ResolutionInputs = append(graph.ResolutionInputs, TransformOutputKey(cwd, input))
|
|
21
|
+
}
|
|
22
|
+
sort.Strings(graph.ResolutionInputs)
|
|
23
|
+
graph.attachObservedInputProof(fs, cwd)
|
|
24
|
+
return graph
|
|
25
|
+
}
|
package/driver/target_ts70.go
CHANGED
|
@@ -9,9 +9,21 @@ import (
|
|
|
9
9
|
ast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
10
|
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
11
11
|
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
12
|
+
shimtsoptions "github.com/microsoft/typescript-go/shim/tsoptions"
|
|
12
13
|
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
13
14
|
)
|
|
14
15
|
|
|
16
|
+
// programRootMembership uses the 7.0 supported-extension APIs and effective
|
|
17
|
+
// parsed options. A facade constructed without parsed config has no report.
|
|
18
|
+
func programRootMembership(prog *Program) *TransformMembership {
|
|
19
|
+
if prog.ParsedConfig == nil || prog.ParsedConfig.CompilerOptions() == nil {
|
|
20
|
+
return nil
|
|
21
|
+
}
|
|
22
|
+
options := prog.ParsedConfig.CompilerOptions()
|
|
23
|
+
groups := shimtsoptions.GetSupportedExtensions(options, nil)
|
|
24
|
+
return transformMembership(prog, shimtsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(options, groups))
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
// TypeScript 7.0 compiler target.
|
|
16
28
|
//
|
|
17
29
|
// The driver's emit and diagnostic glue calls compiler APIs whose signatures
|
package/driver/target_ts71.go
CHANGED
|
@@ -9,9 +9,21 @@ import (
|
|
|
9
9
|
ast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
10
|
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
11
11
|
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
12
|
+
shimtsoptions "github.com/microsoft/typescript-go/shim/tsoptions"
|
|
12
13
|
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
13
14
|
)
|
|
14
15
|
|
|
16
|
+
// programRootMembership uses the 7.1 supported-extension APIs with the Program's
|
|
17
|
+
// actual mapper extensions. No root selection is reimplemented here.
|
|
18
|
+
func programRootMembership(prog *Program) *TransformMembership {
|
|
19
|
+
if prog.ParsedConfig == nil || prog.ParsedConfig.CompilerOptions() == nil {
|
|
20
|
+
return nil
|
|
21
|
+
}
|
|
22
|
+
options := prog.ParsedConfig.CompilerOptions()
|
|
23
|
+
groups := shimtsoptions.GetSupportedExtensions(options, prog.TSProgram.ContentMapperExtensions())
|
|
24
|
+
return transformMembership(prog, shimtsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(options, groups))
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
// TypeScript 7.1 compiler target.
|
|
16
28
|
//
|
|
17
29
|
// The driver's emit and diagnostic glue calls compiler APIs whose signatures
|
|
@@ -15,6 +15,9 @@ import (
|
|
|
15
15
|
// no contributor declarations to require, so every transformed file is listed.
|
|
16
16
|
// This side channel does not independently certify resolver, host or plugin
|
|
17
17
|
// source inputs reported through other envelope fields.
|
|
18
|
+
// Content mapper manifests and upstream-declared dynamic project inputs are
|
|
19
|
+
// added for every file independently of plugin completeness. These declarations
|
|
20
|
+
// carry no compiler-read proof; consumers witness them before a compilation.
|
|
18
21
|
//
|
|
19
22
|
// @evidence contracts/common.md#principled-implementation Reported dependencies and explicit completeness are distinct; an empty contributor set is complete without pretending silent contributors proved anything.
|
|
20
23
|
// @evidence contracts/common.md#clear-and-simple-design One per-file adjacency map and one complete-file list carry separate facts.
|
|
@@ -95,7 +98,25 @@ func (p *Program) TransformDependenciesFor(cwd string) TransformDependencies {
|
|
|
95
98
|
for _, file := range files {
|
|
96
99
|
keys = append(keys, TransformOutputKey(cwd, string(file.FileName())))
|
|
97
100
|
}
|
|
98
|
-
|
|
101
|
+
out := p.plugins.transformDependencies(keys)
|
|
102
|
+
if len(p.mapperDependencies) != 0 {
|
|
103
|
+
for _, key := range keys {
|
|
104
|
+
inputs := map[string]struct{}{}
|
|
105
|
+
for _, input := range out.Dependencies[key] {
|
|
106
|
+
inputs[input] = struct{}{}
|
|
107
|
+
}
|
|
108
|
+
for _, input := range p.mapperDependencies {
|
|
109
|
+
name := TransformOutputKey(cwd, input)
|
|
110
|
+
if name != key {
|
|
111
|
+
inputs[name] = struct{}{}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if len(inputs) != 0 {
|
|
115
|
+
out.Dependencies = appendDependencyEntry(out.Dependencies, key, inputs)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return out
|
|
99
120
|
}
|
|
100
121
|
|
|
101
122
|
// transformDependencies aggregates every contributing plugin's declarations
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
package driver
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"sort"
|
|
5
|
+
"strings"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TransformMembership reports the native settings needed to discover possible
|
|
9
|
+
// roots. Output directories are absolute compiler paths; nil clears an inherited
|
|
10
|
+
// output option. Explicit excludes and files/include specifications remain in
|
|
11
|
+
// the authored configuration. This metadata does not certify a completed walk.
|
|
12
|
+
//
|
|
13
|
+
// @evidence contracts/common.md#principled-implementation Supported extension groups and parsed output paths retain effective native settings independently of JSON-only reconstruction.
|
|
14
|
+
// @evidence contracts/common.md#clear-and-simple-design Three fields express the root-admission dimensions that invocation options can change; config specification ownership stays separate.
|
|
15
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts No source population or completed walk is inferred from this settings report.
|
|
16
|
+
// @evidence contracts/common.md#meaningful-documentation Native prose and fields distinguish possible suffixes, absolute output paths and null clearing.
|
|
17
|
+
// @evidence contracts/portability.md#os-neutral-implementation Native compiler paths retain their spelling; consumers compare them under observed project anchors and compiler case policy.
|
|
18
|
+
// @evidenceExclude contracts/performance.md#efficient-algorithms This declaration chooses no algorithm.
|
|
19
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This metadata owns no cache or reuse verdict.
|
|
20
|
+
// @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The caller-owned report acquires no handle or historical state.
|
|
21
|
+
type TransformMembership struct {
|
|
22
|
+
// InputExtensions preserve possible-root suffix spellings from upstream APIs.
|
|
23
|
+
InputExtensions []string `json:"inputExtensions"`
|
|
24
|
+
// OutDir is the parsed absolute output directory, or nil when unset.
|
|
25
|
+
OutDir *string `json:"outDir"`
|
|
26
|
+
// DeclarationDir is the parsed absolute declaration directory, or nil when unset.
|
|
27
|
+
DeclarationDir *string `json:"declarationDir"`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// transformMembership projects upstream-supported suffixes and parsed paths.
|
|
31
|
+
// Declaration suffixes already covered by their underlying source suffix are
|
|
32
|
+
// redundant for possible-root admission and are omitted. Work follows extension
|
|
33
|
+
// text plus sorting; the returned report owns its slices and path pointers.
|
|
34
|
+
func transformMembership(prog *Program, groups [][]string) *TransformMembership {
|
|
35
|
+
extensions := map[string]struct{}{}
|
|
36
|
+
for _, group := range groups {
|
|
37
|
+
for _, extension := range group {
|
|
38
|
+
extensions[extension] = struct{}{}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for extension := range extensions {
|
|
42
|
+
if strings.HasPrefix(extension, ".d.") {
|
|
43
|
+
if _, covered := extensions[extension[2:]]; covered {
|
|
44
|
+
delete(extensions, extension)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
out := &TransformMembership{InputExtensions: make([]string, 0, len(extensions))}
|
|
49
|
+
for extension := range extensions {
|
|
50
|
+
out.InputExtensions = append(out.InputExtensions, extension)
|
|
51
|
+
}
|
|
52
|
+
sort.Strings(out.InputExtensions)
|
|
53
|
+
options := prog.ParsedConfig.CompilerOptions()
|
|
54
|
+
if value := string(options.OutDir); value != "" {
|
|
55
|
+
out.OutDir = &value
|
|
56
|
+
}
|
|
57
|
+
if value := string(options.DeclarationDir); value != "" {
|
|
58
|
+
out.DeclarationDir = &value
|
|
59
|
+
}
|
|
60
|
+
return out
|
|
61
|
+
}
|
|
@@ -29,7 +29,7 @@ export declare function appendBuildOutput(left: TtscBuildResult, right: TtscBuil
|
|
|
29
29
|
/**
|
|
30
30
|
* Preserve each phase's source closure and refuse contradictory witnesses.
|
|
31
31
|
*
|
|
32
|
-
* @evidence contracts/common.md#principled-implementation
|
|
32
|
+
* @evidence contracts/common.md#principled-implementation Own dictionary entries union every producer's source authority without admitting inherited properties; conflicting content, physical and predicate observations leave an explicit proof failure instead of selecting a later witness. A private null-prototype failure accumulator preserves reserved keys and projects to the established ordinary public object.
|
|
33
33
|
* @evidence contracts/common.md#clear-and-simple-design One graph combiner serves check composition and the later transform phase without reading inputs or creating a Program.
|
|
34
34
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Reported omissions remain omissions; incompatible witnesses cannot be replaced by current filesystem observations.
|
|
35
35
|
* @evidence contracts/common.md#meaningful-documentation The native description states phase composition and the unresolved-witness behavior without claiming execution acceptance.
|