ttsc 0.34.1 → 0.34.3
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/compileraliases/names.go +69 -0
- package/driver/compileraliases/names_test.go +57 -0
- 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/compiler/internal/resolveNativeTypeScriptPackage.d.ts +27 -0
- package/lib/compiler/internal/resolveNativeTypeScriptPackage.js +103 -0
- package/lib/compiler/internal/resolveNativeTypeScriptPackage.js.map +1 -0
- package/lib/compiler/internal/resolveTsgo.d.ts +7 -6
- package/lib/compiler/internal/resolveTsgo.js +16 -35
- package/lib/compiler/internal/resolveTsgo.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/internal/nativeBinary.d.ts +4 -0
- package/lib/internal/nativeBinary.js +6 -1
- package/lib/internal/nativeBinary.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +5 -2
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/plugin/internal/source/pluginBuildVersions.d.ts +8 -7
- package/lib/plugin/internal/source/pluginBuildVersions.js +13 -13
- package/lib/plugin/internal/source/pluginBuildVersions.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +25 -1
- package/package.json +9 -9
- 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/compiler/internal/resolveNativeTypeScriptPackage.ts +110 -0
- package/src/compiler/internal/resolveTsgo.ts +17 -36
- package/src/internal/CompilerResultAdapter.ts +29 -6
- package/src/internal/nativeBinary.ts +4 -0
- package/src/launcher/internal/prepareExecution.ts +7 -2
- package/src/plugin/internal/source/pluginBuildVersions.ts +13 -15
- 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
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Package compileraliases discovers npm aliases by their declared package target.
|
|
2
|
+
package compileraliases
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"sort"
|
|
9
|
+
"strings"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// Names returns declared npm aliases targeting typescript, followed by the
|
|
13
|
+
// ordinary typescript name. The nearest ancestor manifest declaring TypeScript
|
|
14
|
+
// or its aliases owns discovery; installed dependency trees are not scanned.
|
|
15
|
+
// Callers still resolve each name and verify its installed package identity.
|
|
16
|
+
//
|
|
17
|
+
// @evidence contracts/common.md#principled-implementation npm aliases declare a real target independently of their exposed name. Ancestor manifest dependency maps supply those arbitrary names, while installed identity and native version remain with the caller.
|
|
18
|
+
// @evidence contracts/common.md#clear-and-simple-design This package owns declaration discovery once for all Go config evaluators; each evaluator retains its existing Node-style manifest and platform resolution.
|
|
19
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts Only npm's target syntax and the real typescript identity are constants. No alias name, consumer or fixture controls selection.
|
|
20
|
+
// @evidence contracts/common.md#meaningful-documentation The comment defines filename anchors, ancestor traversal, output ordering and the caller's verification obligation; read or parse errors propagate except absent manifests.
|
|
21
|
+
// @evidence contracts/portability.md#os-neutral-implementation filepath.Abs, Dir and Join follow native paths; os.ReadFile closes its handles and no shell or filesystem case assumption is used.
|
|
22
|
+
// @evidence contracts/performance.md#efficient-algorithms The walk parses ancestor manifest bytes and visits D dependency declarations once; A unique aliases require O(A) retained names and O(A log A) sorting. No installed package population is traversed.
|
|
23
|
+
//
|
|
24
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work Mutable project declarations have no invalidation owner here, so every call reads current bytes.
|
|
25
|
+
// @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Synchronous reads retain no handle; the returned name slice transfers to the caller.
|
|
26
|
+
func Names(anchor string) ([]string, error) {
|
|
27
|
+
absolute, err := filepath.Abs(anchor)
|
|
28
|
+
if err != nil {
|
|
29
|
+
return nil, err
|
|
30
|
+
}
|
|
31
|
+
aliases := make(map[string]struct{})
|
|
32
|
+
for directory := filepath.Dir(absolute); ; directory = filepath.Dir(directory) {
|
|
33
|
+
body, err := os.ReadFile(filepath.Join(directory, "package.json"))
|
|
34
|
+
if err != nil && !os.IsNotExist(err) {
|
|
35
|
+
return nil, err
|
|
36
|
+
}
|
|
37
|
+
if err == nil {
|
|
38
|
+
var project map[string]any
|
|
39
|
+
if err := json.Unmarshal(body, &project); err != nil {
|
|
40
|
+
return nil, err
|
|
41
|
+
}
|
|
42
|
+
declaresTypeScript := false
|
|
43
|
+
for _, field := range []string{"dependencies", "devDependencies", "optionalDependencies"} {
|
|
44
|
+
dependencies, _ := project[field].(map[string]any)
|
|
45
|
+
if _, exists := dependencies["typescript"]; exists {
|
|
46
|
+
declaresTypeScript = true
|
|
47
|
+
}
|
|
48
|
+
for name, value := range dependencies {
|
|
49
|
+
specifier, ok := value.(string)
|
|
50
|
+
if name != "typescript" && ok && (specifier == "npm:typescript" || strings.HasPrefix(specifier, "npm:typescript@")) {
|
|
51
|
+
aliases[name] = struct{}{}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if declaresTypeScript || len(aliases) != 0 {
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if filepath.Dir(directory) == directory {
|
|
60
|
+
break
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
names := make([]string, 0, len(aliases)+1)
|
|
64
|
+
for name := range aliases {
|
|
65
|
+
names = append(names, name)
|
|
66
|
+
}
|
|
67
|
+
sort.Strings(names)
|
|
68
|
+
return append(names, "typescript"), nil
|
|
69
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package compileraliases
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"reflect"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestNamesUsesTheNearestCompilerDeclaration verifies npm alias discovery from
|
|
11
|
+
// declarations rather than a reserved exposed name.
|
|
12
|
+
//
|
|
13
|
+
// 1. Declare arbitrary aliases across all supported dependency maps.
|
|
14
|
+
// 2. Discover them from a nested file, excluding a different npm target.
|
|
15
|
+
// 3. Give the nested package its own compiler declaration and require isolation.
|
|
16
|
+
//
|
|
17
|
+
// @evidence contracts/testing.md#behavioral-verification Calls Names over authored manifests and compares the candidate names it returns; an npm target lookalike must be excluded and a workspace's own compiler declaration must stop ancestor alias discovery.
|
|
18
|
+
// @evidence contracts/testing.md#independent-expectations The aliases alpha, middle and zeta target typescript by literal npm syntax; the contract sorts them before the ordinary typescript fallback. A local typescript declaration independently prescribes the singleton fallback.
|
|
19
|
+
// @evidence contracts/testing.md#distinguishing-cases Exercises dependencies, devDependencies and optionalDependencies, an unrelated target, nested discovery, local declaration isolation, malformed metadata failure and an absent local manifest after removal.
|
|
20
|
+
// @evidence contracts/testing.md#execution-ownership This directly discovered Go unit calls Names in-process over t.TempDir inputs. It resolves no installed compiler and starts no process.
|
|
21
|
+
func TestNamesUsesTheNearestCompilerDeclaration(t *testing.T) {
|
|
22
|
+
root := t.TempDir()
|
|
23
|
+
nested := filepath.Join(root, "workspace")
|
|
24
|
+
if err := os.MkdirAll(nested, 0o755); err != nil {
|
|
25
|
+
t.Fatal(err)
|
|
26
|
+
}
|
|
27
|
+
write := func(file, body string) {
|
|
28
|
+
t.Helper()
|
|
29
|
+
if err := os.WriteFile(file, []byte(body), 0o644); err != nil {
|
|
30
|
+
t.Fatal(err)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
write(filepath.Join(root, "package.json"), `{"dependencies":{"zeta":"npm:typescript@7","unrelated":"npm:typescript-helper@7"},"devDependencies":{"alpha":"npm:typescript@next"},"optionalDependencies":{"middle":"npm:typescript"}}`)
|
|
34
|
+
anchor := filepath.Join(nested, "lint.config.ts")
|
|
35
|
+
got, err := Names(anchor)
|
|
36
|
+
want := []string{"alpha", "middle", "zeta", "typescript"}
|
|
37
|
+
if err != nil || !reflect.DeepEqual(got, want) {
|
|
38
|
+
t.Fatalf("Names = %v, %v; want %v", got, err, want)
|
|
39
|
+
}
|
|
40
|
+
manifest := filepath.Join(nested, "package.json")
|
|
41
|
+
write(manifest, `{"devDependencies":{"typescript":"7.0.2"}}`)
|
|
42
|
+
got, err = Names(anchor)
|
|
43
|
+
if err != nil || !reflect.DeepEqual(got, []string{"typescript"}) {
|
|
44
|
+
t.Fatalf("local Names = %v, %v; want [typescript]", got, err)
|
|
45
|
+
}
|
|
46
|
+
write(manifest, "{")
|
|
47
|
+
if _, err := Names(anchor); err == nil {
|
|
48
|
+
t.Fatal("malformed manifest must fail discovery")
|
|
49
|
+
}
|
|
50
|
+
if err := os.Remove(manifest); err != nil {
|
|
51
|
+
t.Fatal(err)
|
|
52
|
+
}
|
|
53
|
+
got, err = Names(anchor)
|
|
54
|
+
if err != nil || !reflect.DeepEqual(got, want) {
|
|
55
|
+
t.Fatalf("restored Names = %v, %v; want %v", got, err, want)
|
|
56
|
+
}
|
|
57
|
+
}
|
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
|