ttsc 0.28.5 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cmd/ttsc/api_compile.go +6 -4
- package/cmd/ttsc/api_transform.go +6 -4
- package/cmd/ttsc/build.go +7 -6
- package/cmd/ttscgraph/serve.go +70 -77
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +26 -0
- package/cmd/ttscgraph/serve_session_tracks_all_module_specifier_forms_test.go +1 -2
- package/driver/graph.go +67 -35
- package/driver/input_observation.go +305 -115
- package/driver/input_observation_test.go +203 -0
- package/driver/program.go +28 -4
- package/driver/resolution_candidates.go +247 -741
- package/driver/resolution_candidates_test.go +341 -0
- package/lib/compiler/internal/project/readProjectConfig.js +9 -7
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/runBuild.js +1 -0
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +4 -0
- package/lib/compiler/internal/sharedHostHelpers.js +11 -1
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +1 -0
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +165 -8
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/internal/projectInputPathIdentity.js +8 -7
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +57 -19
- package/package.json +8 -8
- package/shim/compiler/incremental.go +52 -0
- package/shim/compiler/resolution.go +444 -0
- package/shim/core/shim.go +15 -1
- package/src/compiler/internal/project/readProjectConfig.ts +9 -7
- package/src/compiler/internal/runBuild.ts +2 -0
- package/src/compiler/internal/sharedHostHelpers.ts +14 -0
- package/src/compiler/internal/startResidentTransform.ts +2 -0
- package/src/compiler/internal/transformProjectInMemory.ts +193 -8
- package/src/internal/projectInputPathIdentity.ts +8 -7
- package/src/structures/ITtscCompilerTransformation.ts +65 -19
- package/utility/host.go +8 -7
package/cmd/ttsc/api_compile.go
CHANGED
|
@@ -10,6 +10,7 @@ import (
|
|
|
10
10
|
"encoding/json"
|
|
11
11
|
"flag"
|
|
12
12
|
"fmt"
|
|
13
|
+
"os"
|
|
13
14
|
|
|
14
15
|
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
15
16
|
|
|
@@ -66,10 +67,11 @@ func runAPICompile(args []string) int {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
|
|
69
|
-
ForceEmit:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
ForceEmit: true,
|
|
71
|
+
SemanticConfigPath: os.Getenv(driver.SemanticConfigPathEnv),
|
|
72
|
+
SingleThreaded: *singleThreaded,
|
|
73
|
+
Checkers: *checkers,
|
|
74
|
+
TsgoArgs: tsgoArgs,
|
|
73
75
|
})
|
|
74
76
|
if err != nil {
|
|
75
77
|
fmt.Fprintf(stderr, "ttsc api-compile: %v\n", err)
|
|
@@ -10,6 +10,7 @@ import (
|
|
|
10
10
|
"encoding/json"
|
|
11
11
|
"flag"
|
|
12
12
|
"fmt"
|
|
13
|
+
"os"
|
|
13
14
|
|
|
14
15
|
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
15
16
|
cwdutil "github.com/samchon/ttsc/packages/ttsc/internal/cwd"
|
|
@@ -71,10 +72,11 @@ func runAPITransform(args []string) int {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
|
|
74
|
-
ForceNoEmit:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
ForceNoEmit: true,
|
|
76
|
+
SemanticConfigPath: os.Getenv(driver.SemanticConfigPathEnv),
|
|
77
|
+
SingleThreaded: *singleThreaded,
|
|
78
|
+
Checkers: *checkers,
|
|
79
|
+
TsgoArgs: tsgoArgs,
|
|
78
80
|
})
|
|
79
81
|
if err != nil {
|
|
80
82
|
fmt.Fprintf(stderr, "ttsc api-transform: %v\n", err)
|
package/cmd/ttsc/build.go
CHANGED
|
@@ -69,12 +69,13 @@ func runBuild(args []string) int {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
|
|
72
|
-
ForceEmit:
|
|
73
|
-
ForceNoEmit:
|
|
74
|
-
OutDir:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
ForceEmit: *emit,
|
|
73
|
+
ForceNoEmit: *noEmit,
|
|
74
|
+
OutDir: *outDir,
|
|
75
|
+
SemanticConfigPath: os.Getenv(driver.SemanticConfigPathEnv),
|
|
76
|
+
SingleThreaded: *singleThreaded,
|
|
77
|
+
Checkers: *checkers,
|
|
78
|
+
TsgoArgs: tsgoArgs,
|
|
78
79
|
})
|
|
79
80
|
if err != nil {
|
|
80
81
|
fmt.Fprintf(stderr, "ttsc: %v\n", err)
|
package/cmd/ttscgraph/serve.go
CHANGED
|
@@ -10,6 +10,7 @@ import (
|
|
|
10
10
|
"io"
|
|
11
11
|
"os"
|
|
12
12
|
"path/filepath"
|
|
13
|
+
"runtime"
|
|
13
14
|
"slices"
|
|
14
15
|
"sort"
|
|
15
16
|
"strings"
|
|
@@ -574,7 +575,7 @@ func (s *graphSession) captureState() error {
|
|
|
574
575
|
if err != nil {
|
|
575
576
|
return err
|
|
576
577
|
}
|
|
577
|
-
inputs := auxiliaryInputs(program,
|
|
578
|
+
inputs := auxiliaryInputs(program, s.cwd)
|
|
578
579
|
for _, path := range missingRootInputs(configs, sourceHashes) {
|
|
579
580
|
inputs = append(inputs, auxiliaryInput{path: path})
|
|
580
581
|
}
|
|
@@ -847,23 +848,25 @@ func changedSources(previous map[string][sha256.Size]byte) (map[string]string, b
|
|
|
847
848
|
}
|
|
848
849
|
|
|
849
850
|
type diskState struct {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
851
|
+
DirectoryEntries bool
|
|
852
|
+
Hash [sha256.Size]byte
|
|
853
|
+
Exists bool
|
|
854
|
+
Realpath string
|
|
855
|
+
IdentityOnly bool
|
|
854
856
|
}
|
|
855
857
|
|
|
856
|
-
// auxiliaryInput distinguishes
|
|
858
|
+
// auxiliaryInput distinguishes resolver inputs whose contents select the
|
|
857
859
|
// build universe from the lexical spelling of the source the compiler already
|
|
858
860
|
// selected. The resident source hash owns the latter's contents; this input
|
|
859
861
|
// owns only whether the spelling still reaches the same physical file.
|
|
860
862
|
type auxiliaryInput struct {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
+
directoryEntries bool
|
|
864
|
+
path string
|
|
865
|
+
identityOnly bool
|
|
863
866
|
}
|
|
864
867
|
|
|
865
|
-
// captureDiskStates records the freshness state of
|
|
866
|
-
//
|
|
868
|
+
// captureDiskStates records the freshness state of resolver inputs. Many do
|
|
869
|
+
// not exist, and a module specifier can name a
|
|
867
870
|
// path the host OS cannot even parse (`./style.css?inline`, a `data:` URL on
|
|
868
871
|
// Windows), so any path that is neither a readable file nor a directory is
|
|
869
872
|
// recorded as absent instead of failing the snapshot: the recorded state only
|
|
@@ -871,12 +874,18 @@ type auxiliaryInput struct {
|
|
|
871
874
|
func captureDiskStates(inputs []auxiliaryInput) map[string]diskState {
|
|
872
875
|
states := make(map[string]diskState, len(inputs))
|
|
873
876
|
for _, input := range inputs {
|
|
874
|
-
state := diskState{
|
|
877
|
+
state := diskState{
|
|
878
|
+
DirectoryEntries: input.directoryEntries,
|
|
879
|
+
IdentityOnly: input.identityOnly,
|
|
880
|
+
}
|
|
875
881
|
content, err := os.ReadFile(input.path)
|
|
876
882
|
if err != nil {
|
|
877
883
|
if info, statErr := os.Stat(input.path); statErr == nil && info.IsDir() {
|
|
878
884
|
state.Exists = true
|
|
879
885
|
state.Realpath = diskRealpath(input.path)
|
|
886
|
+
if input.directoryEntries {
|
|
887
|
+
state.Hash = accessibleDirectoryEntriesHash(input.path)
|
|
888
|
+
}
|
|
880
889
|
}
|
|
881
890
|
states[input.path] = state
|
|
882
891
|
continue
|
|
@@ -894,7 +903,11 @@ func captureDiskStates(inputs []auxiliaryInput) map[string]diskState {
|
|
|
894
903
|
func diskStatesChanged(previous map[string]diskState) bool {
|
|
895
904
|
inputs := make([]auxiliaryInput, 0, len(previous))
|
|
896
905
|
for path, state := range previous {
|
|
897
|
-
inputs = append(inputs, auxiliaryInput{
|
|
906
|
+
inputs = append(inputs, auxiliaryInput{
|
|
907
|
+
directoryEntries: state.DirectoryEntries,
|
|
908
|
+
identityOnly: state.IdentityOnly,
|
|
909
|
+
path: path,
|
|
910
|
+
})
|
|
898
911
|
}
|
|
899
912
|
current := captureDiskStates(inputs)
|
|
900
913
|
for path, state := range previous {
|
|
@@ -905,7 +918,7 @@ func diskStatesChanged(previous map[string]diskState) bool {
|
|
|
905
918
|
return false
|
|
906
919
|
}
|
|
907
920
|
|
|
908
|
-
func auxiliaryInputs(program *driver.Program,
|
|
921
|
+
func auxiliaryInputs(program *driver.Program, cwd string) []auxiliaryInput {
|
|
909
922
|
inputs := []auxiliaryInput{
|
|
910
923
|
{path: filepath.Join(cwd, ".gitignore")},
|
|
911
924
|
{path: filepath.Join(cwd, ".git", "info", "exclude")},
|
|
@@ -925,75 +938,17 @@ func auxiliaryInputs(program *driver.Program, configs []*shimtsoptions.ParsedCom
|
|
|
925
938
|
for _, path := range appendAncestorInputs(nil, directory, cwd) {
|
|
926
939
|
inputs = append(inputs, auxiliaryInput{path: path})
|
|
927
940
|
}
|
|
928
|
-
for _, reference := range source.ReferencedFiles {
|
|
929
|
-
for _, path := range driver.FileCandidates(filepath.Join(directory, filepath.FromSlash(reference.FileName))) {
|
|
930
|
-
inputs = append(inputs, auxiliaryInput{path: path})
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
for _, reference := range source.TypeReferenceDirectives {
|
|
934
|
-
for _, path := range driver.TypeReferenceCandidates(configs, directory, cwd, reference.FileName) {
|
|
935
|
-
inputs = append(inputs, auxiliaryInput{path: path})
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
for _, specifier := range driver.SourceModuleSpecifiers(source) {
|
|
939
|
-
context := driver.ModuleResolutionContext{
|
|
940
|
-
Mode: program.TSProgram.GetModeForUsageLocation(source, specifier),
|
|
941
|
-
}
|
|
942
|
-
for _, parsed := range configs {
|
|
943
|
-
if parsed != nil && parsed.ParsedConfig != nil && parsed.ParsedConfig.CompilerOptions != nil {
|
|
944
|
-
context.Options = parsed.ParsedConfig.CompilerOptions
|
|
945
|
-
break
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
resolved := program.TSProgram.GetResolvedModuleFromModuleSpecifier(source, specifier)
|
|
949
|
-
if resolved != nil && resolved.IsResolved() {
|
|
950
|
-
predecessors := driver.ModuleResolutionPredecessors(
|
|
951
|
-
configs,
|
|
952
|
-
directory,
|
|
953
|
-
cwd,
|
|
954
|
-
specifier.Text(),
|
|
955
|
-
resolved.ResolvedFileName,
|
|
956
|
-
program.FS.UseCaseSensitiveFileNames(),
|
|
957
|
-
context,
|
|
958
|
-
)
|
|
959
|
-
for _, path := range predecessors {
|
|
960
|
-
inputs = append(inputs, auxiliaryInput{
|
|
961
|
-
path: path,
|
|
962
|
-
identityOnly: sameExistingAuxiliaryPath(path, resolved.ResolvedFileName),
|
|
963
|
-
})
|
|
964
|
-
}
|
|
965
|
-
continue
|
|
966
|
-
}
|
|
967
|
-
for _, path := range driver.ModuleResolutionCandidates(configs, directory, cwd, specifier.Text(), context) {
|
|
968
|
-
inputs = append(inputs, auxiliaryInput{path: path})
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
941
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
for _, name := range parsed.ParsedConfig.CompilerOptions.Types {
|
|
980
|
-
for _, path := range driver.TypeReferenceCandidates(configs, parsed.GetCurrentDirectory(), cwd, name) {
|
|
981
|
-
inputs = append(inputs, auxiliaryInput{path: path})
|
|
982
|
-
}
|
|
983
|
-
}
|
|
942
|
+
for _, input := range driver.ObserveProgramResolutions(program, cwd).Inputs {
|
|
943
|
+
inputs = append(inputs, auxiliaryInput{
|
|
944
|
+
directoryEntries: input.DirectoryEntries,
|
|
945
|
+
identityOnly: input.IdentityOnly,
|
|
946
|
+
path: input.Path,
|
|
947
|
+
})
|
|
984
948
|
}
|
|
985
949
|
return compactAuxiliaryInputs(inputs)
|
|
986
950
|
}
|
|
987
951
|
|
|
988
|
-
func sameExistingAuxiliaryPath(left, right string) bool {
|
|
989
|
-
leftInfo, err := os.Stat(left)
|
|
990
|
-
if err != nil {
|
|
991
|
-
return false
|
|
992
|
-
}
|
|
993
|
-
rightInfo, err := os.Stat(right)
|
|
994
|
-
return err == nil && os.SameFile(leftInfo, rightInfo)
|
|
995
|
-
}
|
|
996
|
-
|
|
997
952
|
func compactAuxiliaryInputs(inputs []auxiliaryInput) []auxiliaryInput {
|
|
998
953
|
byPath := make(map[string]auxiliaryInput, len(inputs))
|
|
999
954
|
for _, input := range inputs {
|
|
@@ -1001,6 +956,7 @@ func compactAuxiliaryInputs(inputs []auxiliaryInput) []auxiliaryInput {
|
|
|
1001
956
|
continue
|
|
1002
957
|
}
|
|
1003
958
|
if previous, exists := byPath[input.path]; exists {
|
|
959
|
+
input.directoryEntries = previous.directoryEntries || input.directoryEntries
|
|
1004
960
|
// A path that also participates as a manifest or other content-bearing
|
|
1005
961
|
// input keeps the stronger content-sensitive contract.
|
|
1006
962
|
input.identityOnly = previous.identityOnly && input.identityOnly
|
|
@@ -1019,6 +975,43 @@ func compactAuxiliaryInputs(inputs []auxiliaryInput) []auxiliaryInput {
|
|
|
1019
975
|
return output
|
|
1020
976
|
}
|
|
1021
977
|
|
|
978
|
+
// accessibleDirectoryEntriesHash mirrors TypeScript-Go's accessible-entry
|
|
979
|
+
// classification and sorted name order for a directory enumeration input.
|
|
980
|
+
func accessibleDirectoryEntriesHash(directory string) [sha256.Size]byte {
|
|
981
|
+
hash := sha256.New()
|
|
982
|
+
entries, err := os.ReadDir(directory)
|
|
983
|
+
if err == nil {
|
|
984
|
+
for _, entry := range entries {
|
|
985
|
+
kind := byte(0)
|
|
986
|
+
switch {
|
|
987
|
+
case entry.Type().IsDir():
|
|
988
|
+
kind = 'D'
|
|
989
|
+
case entry.Type().IsRegular():
|
|
990
|
+
kind = 'F'
|
|
991
|
+
case entry.Type()&os.ModeSymlink != 0 || (runtime.GOOS == "windows" && entry.Type()&os.ModeIrregular != 0):
|
|
992
|
+
if info, statErr := os.Stat(filepath.Join(directory, entry.Name())); statErr == nil {
|
|
993
|
+
if info.IsDir() {
|
|
994
|
+
kind = 'D'
|
|
995
|
+
} else if info.Mode().IsRegular() {
|
|
996
|
+
kind = 'F'
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
default:
|
|
1000
|
+
continue
|
|
1001
|
+
}
|
|
1002
|
+
if kind == 0 {
|
|
1003
|
+
continue
|
|
1004
|
+
}
|
|
1005
|
+
_, _ = hash.Write([]byte{kind})
|
|
1006
|
+
_, _ = hash.Write([]byte(entry.Name()))
|
|
1007
|
+
_, _ = hash.Write([]byte{0})
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
var digest [sha256.Size]byte
|
|
1011
|
+
copy(digest[:], hash.Sum(nil))
|
|
1012
|
+
return digest
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1022
1015
|
func appendAncestorInputs(inputs []string, directory, stop string) []string {
|
|
1023
1016
|
stop = filepath.Clean(stop)
|
|
1024
1017
|
for current := filepath.Clean(directory); ; current = filepath.Dir(current) {
|
|
@@ -220,6 +220,32 @@ func TestServeSessionReloadsSupersedingModuleCandidates(t *testing.T) {
|
|
|
220
220
|
}
|
|
221
221
|
})
|
|
222
222
|
}
|
|
223
|
+
|
|
224
|
+
t.Run("automatic_type_directory_membership", func(t *testing.T) {
|
|
225
|
+
root := t.TempDir()
|
|
226
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
227
|
+
"compilerOptions": { "module": "commonjs", "target": "ES2022", "types": ["*"] },
|
|
228
|
+
"files": ["src/main.ts"]
|
|
229
|
+
}`)
|
|
230
|
+
writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "export const value = 1;\n")
|
|
231
|
+
session, err := newGraphSession(root, "tsconfig.json")
|
|
232
|
+
if err != nil {
|
|
233
|
+
t.Fatal(err)
|
|
234
|
+
}
|
|
235
|
+
defer session.Close()
|
|
236
|
+
if _, _, _, err := session.Snapshot(); err != nil {
|
|
237
|
+
t.Fatal(err)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
writeGraphFile(t, filepath.Join(root, "node_modules", "@types", "generated", "index.d.ts"), "declare const generatedAmbient: string;\n")
|
|
241
|
+
dump, mode, changed, err := session.Snapshot()
|
|
242
|
+
if err != nil {
|
|
243
|
+
t.Fatal(err)
|
|
244
|
+
}
|
|
245
|
+
if dump == nil || mode != serveModeReload || !changed {
|
|
246
|
+
t.Fatalf("automatic type package appearance = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
|
|
247
|
+
}
|
|
248
|
+
})
|
|
223
249
|
}
|
|
224
250
|
|
|
225
251
|
func hasDumpNodeAt(dump graph.Dump, name, fileSuffix string) bool {
|
|
@@ -19,7 +19,6 @@ import legacy = require("./legacy");
|
|
|
19
19
|
export { exported } from "./exported";
|
|
20
20
|
export type Deferred = import("./types").Deferred;
|
|
21
21
|
export const lazy = import("./lazy");
|
|
22
|
-
export const common = require("./common");
|
|
23
22
|
export const result = legacy();
|
|
24
23
|
`)
|
|
25
24
|
|
|
@@ -29,7 +28,7 @@ export const result = legacy();
|
|
|
29
28
|
}
|
|
30
29
|
defer session.Close()
|
|
31
30
|
|
|
32
|
-
for _, name := range []string{"legacy.ts", "exported.ts", "types.ts", "lazy.ts"
|
|
31
|
+
for _, name := range []string{"legacy.ts", "exported.ts", "types.ts", "lazy.ts"} {
|
|
33
32
|
candidate := filepath.Join(root, "src", name)
|
|
34
33
|
state, ok := session.auxStates[candidate]
|
|
35
34
|
if !ok {
|
package/driver/graph.go
CHANGED
|
@@ -32,26 +32,31 @@ const bundledScheme = "bundled:///"
|
|
|
32
32
|
// entries). A change to any of them can affect every file.
|
|
33
33
|
// - Configs lists the project tsconfig followed by its `extends` ancestry.
|
|
34
34
|
// - Candidates maps each importing file to the resolution probes that precede
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// -
|
|
39
|
-
// state
|
|
35
|
+
// or otherwise participate in its selected module and type-reference
|
|
36
|
+
// results. They are a separate class from resolved edges: a predicate or
|
|
37
|
+
// package manifest changing can change an unchanged reference's meaning.
|
|
38
|
+
// - ResolutionInputs lists automatic type discovery and resolution inputs
|
|
39
|
+
// whose state can affect every source file, including type-root directory
|
|
40
|
+
// membership.
|
|
41
|
+
// - InputObservations preserves the independent filesystem predicates the
|
|
42
|
+
// compiler actually asked. InputHashes and InputRealpaths retain the legacy
|
|
43
|
+
// collapsed content/identity projection for older consumers.
|
|
40
44
|
// - InputProofFailures gives a stable reason when a realized member lacks
|
|
41
|
-
//
|
|
42
|
-
// never expected to read them.
|
|
45
|
+
// proof or a replayed resolver predicate changed.
|
|
43
46
|
//
|
|
44
47
|
// Keys and values use the same convention as the envelope's `typescript`
|
|
45
48
|
// map: project-relative slash paths, falling back to slash-normalized
|
|
46
49
|
// absolute paths outside the project root (see TransformOutputKey).
|
|
47
50
|
type TransformGraph struct {
|
|
48
|
-
Edges map[string][]string
|
|
49
|
-
Globals []string
|
|
50
|
-
Configs []string
|
|
51
|
-
Candidates map[string][]string
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
Edges map[string][]string `json:"edges"`
|
|
52
|
+
Globals []string `json:"globals"`
|
|
53
|
+
Configs []string `json:"configs"`
|
|
54
|
+
Candidates map[string][]string `json:"candidates,omitempty"`
|
|
55
|
+
ResolutionInputs []string `json:"resolutionInputs,omitempty"`
|
|
56
|
+
InputObservations map[string]TransformInputObservation `json:"inputObservations,omitempty"`
|
|
57
|
+
InputHashes map[string]*string `json:"inputHashes,omitempty"`
|
|
58
|
+
InputRealpaths map[string]*string `json:"inputRealpaths,omitempty"`
|
|
59
|
+
InputProofFailures map[string]string `json:"inputProofFailures,omitempty"`
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
// NewTransformGraph computes the reference graph of a loaded program, keyed
|
|
@@ -63,11 +68,13 @@ func NewTransformGraph(prog *Program, cwd string) *TransformGraph {
|
|
|
63
68
|
if prog == nil || prog.TSProgram == nil {
|
|
64
69
|
return nil
|
|
65
70
|
}
|
|
71
|
+
resolution := ObserveProgramResolutions(prog, cwd)
|
|
66
72
|
graph := &TransformGraph{
|
|
67
|
-
Edges:
|
|
68
|
-
Globals:
|
|
69
|
-
Configs:
|
|
70
|
-
Candidates:
|
|
73
|
+
Edges: map[string][]string{},
|
|
74
|
+
Globals: []string{},
|
|
75
|
+
Configs: []string{},
|
|
76
|
+
Candidates: resolution.Candidates,
|
|
77
|
+
ResolutionInputs: resolution.Universal,
|
|
71
78
|
}
|
|
72
79
|
for _, file := range prog.TSProgram.SourceFiles() {
|
|
73
80
|
fileName := file.FileName()
|
|
@@ -87,6 +94,10 @@ func NewTransformGraph(prog *Program, cwd string) *TransformGraph {
|
|
|
87
94
|
}
|
|
88
95
|
sort.Strings(graph.Globals)
|
|
89
96
|
graph.Configs = configChain(prog, cwd)
|
|
97
|
+
resolution.ApplyUniversalResolutionFailure(graph.Edges)
|
|
98
|
+
if len(resolution.Failures) != 0 {
|
|
99
|
+
graph.InputProofFailures = resolution.Failures
|
|
100
|
+
}
|
|
90
101
|
graph.attachInputProof(prog, cwd)
|
|
91
102
|
return graph
|
|
92
103
|
}
|
|
@@ -123,21 +134,39 @@ func (graph *TransformGraph) attachInputProof(prog *Program, cwd string) {
|
|
|
123
134
|
inputs[candidate] = struct{}{}
|
|
124
135
|
}
|
|
125
136
|
}
|
|
137
|
+
for _, input := range graph.ResolutionInputs {
|
|
138
|
+
inputs[input] = struct{}{}
|
|
139
|
+
}
|
|
126
140
|
hashes := map[string]*string{}
|
|
127
141
|
realpaths := map[string]*string{}
|
|
128
|
-
|
|
142
|
+
observations := map[string]TransformInputObservation{}
|
|
143
|
+
failures := graph.InputProofFailures
|
|
144
|
+
if failures == nil {
|
|
145
|
+
failures = map[string]string{}
|
|
146
|
+
}
|
|
129
147
|
for input := range inputs {
|
|
130
148
|
file := filepath.FromSlash(input)
|
|
131
149
|
if !filepath.IsAbs(file) {
|
|
132
150
|
file = filepath.Join(cwd, file)
|
|
133
151
|
}
|
|
134
|
-
|
|
135
|
-
if
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
observation, predicateFailure := prog.inputObserver.predicateProof(file)
|
|
153
|
+
if predicateFailure == "" {
|
|
154
|
+
observations[input] = observation
|
|
155
|
+
}
|
|
156
|
+
var hash, realpath *string
|
|
157
|
+
legacyFailure := predicateFailure
|
|
158
|
+
if predicateFailure == "" {
|
|
159
|
+
hash, realpath, legacyFailure = prog.inputObserver.proof(file)
|
|
160
|
+
}
|
|
161
|
+
if legacyFailure != "" {
|
|
162
|
+
// A resolver input can have a complete predicate proof that the legacy
|
|
163
|
+
// path-kind projection cannot represent, such as a successful file check
|
|
164
|
+
// whose content was never requested. The rich proof remains sufficient
|
|
165
|
+
// for that input; only a predicate failure, or any realized-input
|
|
166
|
+
// failure, makes the generation inadmissible.
|
|
167
|
+
_, isRealized := realized[input]
|
|
168
|
+
if isRealized || (predicateFailure != "" && predicateFailure != inputProofUnobserved) {
|
|
169
|
+
failures[input] = string(legacyFailure)
|
|
141
170
|
}
|
|
142
171
|
continue
|
|
143
172
|
}
|
|
@@ -148,6 +177,9 @@ func (graph *TransformGraph) attachInputProof(prog *Program, cwd string) {
|
|
|
148
177
|
graph.InputHashes = hashes
|
|
149
178
|
graph.InputRealpaths = realpaths
|
|
150
179
|
}
|
|
180
|
+
if len(observations) != 0 {
|
|
181
|
+
graph.InputObservations = observations
|
|
182
|
+
}
|
|
151
183
|
if len(failures) != 0 {
|
|
152
184
|
graph.InputProofFailures = failures
|
|
153
185
|
}
|
|
@@ -158,18 +190,18 @@ func (graph *TransformGraph) attachInputProof(prog *Program, cwd string) {
|
|
|
158
190
|
func referenceTargets(prog *Program, cwd string, file *ast.SourceFile) []string {
|
|
159
191
|
paths := shimcompiler.GetReferencedFilePaths(prog.TSProgram, file)
|
|
160
192
|
targets := make([]string, 0, len(paths))
|
|
161
|
-
for _,
|
|
162
|
-
fileName := path
|
|
193
|
+
for _, referencedPath := range paths {
|
|
163
194
|
// Referenced paths are case-canonicalized tspath.Path values; recover the
|
|
164
|
-
// real spelling from the
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
195
|
+
// resident source and its real spelling from the Program. The incremental
|
|
196
|
+
// helper can retain a raw extensionless project-reference directive even
|
|
197
|
+
// when no corresponding source became resident. That spelling is a
|
|
198
|
+
// resolver candidate, not a realized graph edge, and has no compiler-time
|
|
199
|
+
// content proof.
|
|
200
|
+
resolved := prog.TSProgram.GetSourceFileByPath(shimtspath.Path(referencedPath))
|
|
201
|
+
if resolved == nil || resolved == file || strings.HasPrefix(resolved.FileName(), bundledScheme) {
|
|
170
202
|
continue
|
|
171
203
|
}
|
|
172
|
-
targets = append(targets, TransformOutputKey(cwd,
|
|
204
|
+
targets = append(targets, TransformOutputKey(cwd, resolved.FileName()))
|
|
173
205
|
}
|
|
174
206
|
sort.Strings(targets)
|
|
175
207
|
return targets
|