ttsc 0.19.0 → 0.19.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/README.md +68 -186
- package/cmd/graphdump/main.go +21 -1
- package/cmd/ttsc/api_compile.go +3 -13
- package/cmd/ttsc/api_transform.go +13 -0
- package/cmd/ttscgraph/dump.go +40 -1
- package/cmd/ttscgraph/serve.go +194 -25
- package/cmd/ttscgraph/serve_error_response_still_names_its_mode_test.go +73 -0
- package/cmd/ttscgraph/serve_protocol_fails_the_stream_on_an_unaddressable_request_line_test.go +43 -0
- package/cmd/ttscgraph/serve_protocol_version_matches_the_typescript_client_test.go +48 -0
- package/cmd/ttscgraph/serve_snapshot_proves_its_program_without_a_second_read_test.go +109 -0
- package/driver/graph.go +133 -0
- package/internal/graph/diagnostics.go +6 -5
- package/internal/graph/dump.go +69 -17
- package/internal/graph/dump_diagnostics_ride_the_generation_that_built_it_test.go +79 -0
- package/internal/graph/dump_evidence_skips_leading_trivia_test.go +1 -1
- package/internal/graph/dump_is_a_function_of_the_source_test.go +1 -1
- package/internal/graph/dump_never_serializes_a_list_as_null_test.go +76 -0
- package/internal/graph/dump_schema_version_matches_the_typescript_client_test.go +48 -0
- package/internal/graph/dump_serializes_the_full_graph_test.go +21 -7
- package/internal/graph/node_modifiers_emit_union_strings_test.go +1 -1
- package/internal/graph/provenance.go +312 -0
- package/lib/TtscCompiler.js +9 -4
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.d.ts +4 -0
- package/lib/compiler/internal/transformProjectInMemory.js +67 -12
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +98 -0
- package/package.json +8 -8
- package/shim/compiler/incremental.go +58 -0
- package/shim/core/shim.go +5 -0
- package/src/TtscCompiler.ts +19 -4
- package/src/compiler/internal/transformProjectInMemory.ts +106 -12
- package/src/structures/ITtscCompilerTransformation.ts +107 -0
- package/tools/shim_audit/baseline.json +2 -1
- package/tools/shim_audit/main.go +17 -0
- package/utility/host.go +17 -15
- package/cmd/ttscgraph/serve_protocol_rejects_invalid_request_line_test.go +0 -43
|
@@ -9,6 +9,39 @@ import type { ITtscCompilerDiagnostic } from "./ITtscCompilerDiagnostic";
|
|
|
9
9
|
*/
|
|
10
10
|
export type ITtscCompilerTransformation = ITtscCompilerTransformation.ISuccess | ITtscCompilerTransformation.IFailure | ITtscCompilerTransformation.IException;
|
|
11
11
|
export declare namespace ITtscCompilerTransformation {
|
|
12
|
+
/**
|
|
13
|
+
* Host-owned reference graph of the transformed program, mirroring the
|
|
14
|
+
* envelope's optional `graph` section.
|
|
15
|
+
*
|
|
16
|
+
* The graph is the language-semantic input bound of the transform under `tsc
|
|
17
|
+
* --incremental` semantics: any symbol a file can reference is reachable
|
|
18
|
+
* through its import/reference closure or is ambient. Bundler adapters
|
|
19
|
+
* register, per transformed file `F`, the reachability closure of
|
|
20
|
+
* {@link edges} from `F` together with {@link globals} and {@link configs}, so
|
|
21
|
+
* persistent caches and watch graphs invalidate soundly without per-plugin
|
|
22
|
+
* dependency reporting.
|
|
23
|
+
*
|
|
24
|
+
* Keys and values follow the same convention as {@link ISuccess.typescript}:
|
|
25
|
+
* project-relative slash paths, falling back to absolute slash paths outside
|
|
26
|
+
* the project root.
|
|
27
|
+
*/
|
|
28
|
+
interface IReferenceGraph {
|
|
29
|
+
/**
|
|
30
|
+
* Direct resolved references per file: imports, re-exports, `///
|
|
31
|
+
* <reference>` targets, and type reference directives — type-only edges
|
|
32
|
+
* included. Direct edges only; consumers compute transitive reachability
|
|
33
|
+
* themselves.
|
|
34
|
+
*/
|
|
35
|
+
edges: Record<string, string[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Files contributing to the global scope (ambient declaration files, script
|
|
38
|
+
* files, global augmentations, `typeRoots` entries). A change to any of
|
|
39
|
+
* them can affect every file in the program.
|
|
40
|
+
*/
|
|
41
|
+
globals: string[];
|
|
42
|
+
/** The project tsconfig followed by its `extends` ancestry. */
|
|
43
|
+
configs: string[];
|
|
44
|
+
}
|
|
12
45
|
/** Successful source-to-source transformation result. */
|
|
13
46
|
interface ISuccess {
|
|
14
47
|
/** Indicates that transformation completed without diagnostics. */
|
|
@@ -36,6 +69,54 @@ export declare namespace ITtscCompilerTransformation {
|
|
|
36
69
|
* invalidation. ttsc passes the paths through verbatim.
|
|
37
70
|
*/
|
|
38
71
|
dependencies?: Record<string, string[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Transformed files (keyed like {@link typescript}) whose
|
|
74
|
+
* {@link dependencies} entry the transform host declares **complete**: every
|
|
75
|
+
* input beyond the file itself and the universal
|
|
76
|
+
* {@link IReferenceGraph.configs} chain is listed there.
|
|
77
|
+
*
|
|
78
|
+
* The declaration narrows invalidation. For a listed file a consumer uses
|
|
79
|
+
* `dependencies[F] ∪ graph.configs` instead of the union with the
|
|
80
|
+
* host-owned `reach(graph.edges, F) ∪ graph.globals` bound, so a change to
|
|
81
|
+
* a file the transform never consulted no longer re-runs it. Unlisted files
|
|
82
|
+
* keep the union, so a mixed envelope composes per file.
|
|
83
|
+
*
|
|
84
|
+
* This is a responsibility transfer, not a hint: an omission makes the
|
|
85
|
+
* consumer serve stale output, and that is a defect of the declaring plugin
|
|
86
|
+
* rather than of the host. Producers list a file only when the reported set
|
|
87
|
+
* is derived from what the transform actually consulted (a Checker-driven
|
|
88
|
+
* generator's per-file consulted-declaration list). When several plugin
|
|
89
|
+
* entries contribute to one file, the envelope's author may list it only if
|
|
90
|
+
* every contributing entry declared its own list complete for it.
|
|
91
|
+
*
|
|
92
|
+
* The config chain a listed file keeps is {@link graph}'s, so a host that
|
|
93
|
+
* declares completeness should stamp {@link graph} too; without it a listed
|
|
94
|
+
* file retains no universal input at all.
|
|
95
|
+
*
|
|
96
|
+
* Optional, and never required for correctness: an envelope without this
|
|
97
|
+
* field keeps the sound host-owned bound. A file that is both listed here
|
|
98
|
+
* and in {@link volatile} keeps the union, since the two claims contradict
|
|
99
|
+
* and the conservative one wins.
|
|
100
|
+
*/
|
|
101
|
+
dependenciesComplete?: string[];
|
|
102
|
+
/**
|
|
103
|
+
* Host-owned reference graph of the transformed program.
|
|
104
|
+
*
|
|
105
|
+
* Optional: only present when the transform host stamped a `graph` section
|
|
106
|
+
* into its stdout envelope (the built-in native host and the linked-plugin
|
|
107
|
+
* host always do; external sidecars adopt through the driver SDK).
|
|
108
|
+
* Malformed sections are dropped, never fatal — the field is advisory
|
|
109
|
+
* invalidation metadata, not output.
|
|
110
|
+
*/
|
|
111
|
+
graph?: IReferenceGraph;
|
|
112
|
+
/**
|
|
113
|
+
* Transformed files (keyed like {@link typescript}) whose output depends on
|
|
114
|
+
* non-file inputs (environment, time, network) as declared by the transform
|
|
115
|
+
* plugin via the envelope's optional `volatile` list. No file-dependency
|
|
116
|
+
* scheme can represent such inputs, so consumers must exclude these files
|
|
117
|
+
* from caching instead of watching more files.
|
|
118
|
+
*/
|
|
119
|
+
volatile?: string[];
|
|
39
120
|
}
|
|
40
121
|
/** Source-to-source transformation result that completed with diagnostics. */
|
|
41
122
|
interface IFailure {
|
|
@@ -57,6 +138,23 @@ export declare namespace ITtscCompilerTransformation {
|
|
|
57
138
|
* transform did not complete its pass.
|
|
58
139
|
*/
|
|
59
140
|
dependencies?: Record<string, string[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Files whose reported dependency list the host declares complete. Same
|
|
143
|
+
* shape and semantics as {@link ISuccess.dependenciesComplete}; may be
|
|
144
|
+
* partial when the transform did not complete its pass.
|
|
145
|
+
*/
|
|
146
|
+
dependenciesComplete?: string[];
|
|
147
|
+
/**
|
|
148
|
+
* Host-owned reference graph. Same shape and semantics as
|
|
149
|
+
* {@link ISuccess.graph}; may be absent when diagnostics prevented the host
|
|
150
|
+
* from loading the program.
|
|
151
|
+
*/
|
|
152
|
+
graph?: IReferenceGraph;
|
|
153
|
+
/**
|
|
154
|
+
* Volatile transformed files. Same shape and semantics as
|
|
155
|
+
* {@link ISuccess.volatile}.
|
|
156
|
+
*/
|
|
157
|
+
volatile?: string[];
|
|
60
158
|
}
|
|
61
159
|
/** Unexpected host-level error during transformation. */
|
|
62
160
|
interface IException {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ttsc",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "General-purpose TypeScript-Go compiler, runtime, plugin host, and LSP host.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"typescript": "^7.0.2"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@ttsc/linux-x64": "0.19.
|
|
39
|
-
"@ttsc/
|
|
40
|
-
"@ttsc/linux-arm64": "0.19.
|
|
41
|
-
"@ttsc/darwin-
|
|
42
|
-
"@ttsc/
|
|
43
|
-
"@ttsc/win32-
|
|
44
|
-
"@ttsc/
|
|
38
|
+
"@ttsc/linux-x64": "0.19.2",
|
|
39
|
+
"@ttsc/linux-arm": "0.19.2",
|
|
40
|
+
"@ttsc/linux-arm64": "0.19.2",
|
|
41
|
+
"@ttsc/darwin-x64": "0.19.2",
|
|
42
|
+
"@ttsc/win32-x64": "0.19.2",
|
|
43
|
+
"@ttsc/win32-arm64": "0.19.2",
|
|
44
|
+
"@ttsc/darwin-arm64": "0.19.2"
|
|
45
45
|
},
|
|
46
46
|
"sideEffects": false,
|
|
47
47
|
"files": [
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// gen_shims:hand-maintained
|
|
2
|
+
//
|
|
3
|
+
// Reference-graph parts of tsgo's own incremental engine (`tsc --incremental`
|
|
4
|
+
// semantics), linked from internal/execute/incremental so ttsc's transform
|
|
5
|
+
// envelopes can carry the same language-semantic input bound the compiler
|
|
6
|
+
// itself uses for invalidation: per-file direct resolved references and the
|
|
7
|
+
// files that contribute to the global scope.
|
|
8
|
+
package compiler
|
|
9
|
+
|
|
10
|
+
import (
|
|
11
|
+
_ "unsafe"
|
|
12
|
+
|
|
13
|
+
innerast "github.com/microsoft/typescript-go/internal/ast"
|
|
14
|
+
"github.com/microsoft/typescript-go/internal/collections"
|
|
15
|
+
innercompiler "github.com/microsoft/typescript-go/internal/compiler"
|
|
16
|
+
"github.com/microsoft/typescript-go/internal/tspath"
|
|
17
|
+
|
|
18
|
+
// The linknamed symbols below live in the incremental package; the blank
|
|
19
|
+
// import compiles it into every shim consumer so the references resolve.
|
|
20
|
+
_ "github.com/microsoft/typescript-go/internal/execute/incremental"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
//go:linkname incrementalGetReferencedFiles github.com/microsoft/typescript-go/internal/execute/incremental.getReferencedFiles
|
|
24
|
+
func incrementalGetReferencedFiles(program *innercompiler.Program, file *innerast.SourceFile) *collections.Set[tspath.Path]
|
|
25
|
+
|
|
26
|
+
//go:linkname incrementalFileAffectsGlobalScope github.com/microsoft/typescript-go/internal/execute/incremental.fileAffectsGlobalScope
|
|
27
|
+
func incrementalFileAffectsGlobalScope(file *innerast.SourceFile) bool
|
|
28
|
+
|
|
29
|
+
// GetReferencedFilePaths returns the canonical paths of every file that `file`
|
|
30
|
+
// directly references in `program`: resolved imports and re-exports (type-only
|
|
31
|
+
// included), `/// <reference>` targets, resolved type reference directives,
|
|
32
|
+
// module augmentations, and ambient-module declaration files. This is exactly
|
|
33
|
+
// the per-file `referencedMap` entry tsgo's incremental engine stores in
|
|
34
|
+
// `tsbuildinfo`, so the result is the sound language-semantic upper bound on
|
|
35
|
+
// which program files a symbol in `file` can resolve through.
|
|
36
|
+
//
|
|
37
|
+
// The returned strings are tspath.Path values (case-canonicalized on
|
|
38
|
+
// case-insensitive filesystems); map them back to real file names through
|
|
39
|
+
// Program.GetSourceFileByPath when the original spelling matters.
|
|
40
|
+
func GetReferencedFilePaths(program *Program, file *innerast.SourceFile) []string {
|
|
41
|
+
set := incrementalGetReferencedFiles(program, file)
|
|
42
|
+
if set == nil {
|
|
43
|
+
return nil
|
|
44
|
+
}
|
|
45
|
+
out := make([]string, 0, set.Len())
|
|
46
|
+
for path := range set.Keys() {
|
|
47
|
+
out = append(out, string(path))
|
|
48
|
+
}
|
|
49
|
+
return out
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// FileAffectsGlobalScope reports whether editing `file` can change the global
|
|
53
|
+
// scope: global-scope module augmentations, ambient declaration files, and
|
|
54
|
+
// script (non-module) files. Mirrors the predicate tsgo's incremental engine
|
|
55
|
+
// uses to decide that a change must invalidate every file in the program.
|
|
56
|
+
func FileAffectsGlobalScope(file *innerast.SourceFile) bool {
|
|
57
|
+
return incrementalFileAffectsGlobalScope(file)
|
|
58
|
+
}
|
package/shim/core/shim.go
CHANGED
|
@@ -50,6 +50,11 @@ const (
|
|
|
50
50
|
JsxEmitReactJSXDev = innercore.JsxEmitReactJSXDev
|
|
51
51
|
)
|
|
52
52
|
|
|
53
|
+
// Version reports the TypeScript compiler version typescript-go implements, in
|
|
54
|
+
// the same form `tsc --version` prints. A graph snapshot publishes it so a
|
|
55
|
+
// consumer can tell which checker resolved the facts it is reading.
|
|
56
|
+
func Version() string { return innercore.Version() }
|
|
57
|
+
|
|
53
58
|
// NewTextRange constructs a closed/open [pos, end) text range.
|
|
54
59
|
func NewTextRange(pos, end int) TextRange { return innercore.NewTextRange(pos, end) }
|
|
55
60
|
|
package/src/TtscCompiler.ts
CHANGED
|
@@ -259,8 +259,11 @@ interface ProjectResult {
|
|
|
259
259
|
|
|
260
260
|
interface ProjectTransformation {
|
|
261
261
|
dependencies?: Record<string, string[]>;
|
|
262
|
+
dependenciesComplete?: string[];
|
|
263
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
262
264
|
result: TtscBuildResult;
|
|
263
265
|
typescript: Record<string, string>;
|
|
266
|
+
volatile?: string[];
|
|
264
267
|
}
|
|
265
268
|
|
|
266
269
|
function runProject(task: () => ProjectResult): ITtscCompilerResult {
|
|
@@ -371,20 +374,32 @@ function toCompilerResult(project: ProjectResult): ITtscCompilerResult {
|
|
|
371
374
|
function toCompilerTransformation(
|
|
372
375
|
project: ProjectTransformation,
|
|
373
376
|
): ITtscCompilerTransformation {
|
|
374
|
-
const {
|
|
375
|
-
|
|
377
|
+
const {
|
|
378
|
+
dependencies,
|
|
379
|
+
dependenciesComplete,
|
|
380
|
+
graph,
|
|
381
|
+
result,
|
|
382
|
+
typescript,
|
|
383
|
+
volatile,
|
|
384
|
+
} = project;
|
|
385
|
+
const advisoryFields = {
|
|
386
|
+
...(dependencies === undefined ? {} : { dependencies }),
|
|
387
|
+
...(dependenciesComplete === undefined ? {} : { dependenciesComplete }),
|
|
388
|
+
...(graph === undefined ? {} : { graph }),
|
|
389
|
+
...(volatile === undefined ? {} : { volatile }),
|
|
390
|
+
};
|
|
376
391
|
if (result.status === 0 && !hasErrorDiagnostics(result.diagnostics)) {
|
|
377
392
|
return {
|
|
378
393
|
...(result.diagnostics.length === 0
|
|
379
394
|
? {}
|
|
380
395
|
: { diagnostics: result.diagnostics }),
|
|
381
|
-
...
|
|
396
|
+
...advisoryFields,
|
|
382
397
|
type: "success",
|
|
383
398
|
typescript,
|
|
384
399
|
};
|
|
385
400
|
}
|
|
386
401
|
return {
|
|
387
|
-
...
|
|
402
|
+
...advisoryFields,
|
|
388
403
|
diagnostics:
|
|
389
404
|
result.diagnostics.length === 0
|
|
390
405
|
? [createProcessDiagnostic(result)]
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "../../plugin/internal/loadProjectPlugins";
|
|
7
7
|
import type { ITtscCompilerContext } from "../../structures/ITtscCompilerContext";
|
|
8
8
|
import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiagnostic";
|
|
9
|
+
import type { ITtscCompilerTransformation } from "../../structures/ITtscCompilerTransformation";
|
|
9
10
|
import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoadedNativePlugin";
|
|
10
11
|
import type { ITtscParsedProjectConfig } from "../../structures/internal/ITtscParsedProjectConfig";
|
|
11
12
|
import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
|
|
@@ -41,8 +42,11 @@ import { outputText, spawnNative } from "./spawnNative";
|
|
|
41
42
|
*/
|
|
42
43
|
export function transformProjectInMemory(options: ITtscCompilerContext): {
|
|
43
44
|
dependencies?: Record<string, string[]>;
|
|
45
|
+
dependenciesComplete?: string[];
|
|
46
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
44
47
|
result: TtscBuildResult;
|
|
45
48
|
typescript: Record<string, string>;
|
|
49
|
+
volatile?: string[];
|
|
46
50
|
} {
|
|
47
51
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
48
52
|
const project = readProjectConfig({
|
|
@@ -74,8 +78,11 @@ function transformProjectWithNativeHost(
|
|
|
74
78
|
project: ITtscParsedProjectConfig,
|
|
75
79
|
): {
|
|
76
80
|
dependencies?: Record<string, string[]>;
|
|
81
|
+
dependenciesComplete?: string[];
|
|
82
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
77
83
|
result: TtscBuildResult;
|
|
78
84
|
typescript: Record<string, string>;
|
|
85
|
+
volatile?: string[];
|
|
79
86
|
} {
|
|
80
87
|
const binary = buildNativeCompiler({
|
|
81
88
|
cacheBaseDir: project.root,
|
|
@@ -101,9 +108,7 @@ function transformProjectWithNativeHost(
|
|
|
101
108
|
outputText(res.stderr),
|
|
102
109
|
);
|
|
103
110
|
return {
|
|
104
|
-
...(output
|
|
105
|
-
? {}
|
|
106
|
-
: { dependencies: output.dependencies }),
|
|
111
|
+
...envelopeSideChannels(output),
|
|
107
112
|
result: {
|
|
108
113
|
diagnostics: output.diagnostics,
|
|
109
114
|
status: res.status ?? 1,
|
|
@@ -120,8 +125,11 @@ function transformProjectWithPlugins(
|
|
|
120
125
|
project: ITtscParsedProjectConfig,
|
|
121
126
|
): {
|
|
122
127
|
dependencies?: Record<string, string[]>;
|
|
128
|
+
dependenciesComplete?: string[];
|
|
129
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
123
130
|
result: TtscBuildResult;
|
|
124
131
|
typescript: Record<string, string>;
|
|
132
|
+
volatile?: string[];
|
|
125
133
|
} {
|
|
126
134
|
const loaded = loadProjectPlugins({
|
|
127
135
|
binary: resolveBinary(options) ?? "",
|
|
@@ -159,9 +167,7 @@ function transformProjectWithPlugins(
|
|
|
159
167
|
if (transformers.length === 0) {
|
|
160
168
|
const transformed = transformProjectWithNativeHost(options, project);
|
|
161
169
|
return {
|
|
162
|
-
...(transformed
|
|
163
|
-
? {}
|
|
164
|
-
: { dependencies: transformed.dependencies }),
|
|
170
|
+
...envelopeSideChannels(transformed),
|
|
165
171
|
result: appendBuildOutput(checked, transformed.result),
|
|
166
172
|
typescript: transformed.typescript,
|
|
167
173
|
};
|
|
@@ -196,12 +202,39 @@ function transformProjectWithPlugins(
|
|
|
196
202
|
stdout: "",
|
|
197
203
|
stderr: outputText(res.stderr),
|
|
198
204
|
};
|
|
205
|
+
return {
|
|
206
|
+
...envelopeSideChannels(output),
|
|
207
|
+
result: appendBuildOutput(checked, result),
|
|
208
|
+
typescript: output.typescript,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Collect the optional advisory envelope fields (`dependencies`,
|
|
214
|
+
* `dependenciesComplete`, `graph`, `volatile`) into a spreadable object,
|
|
215
|
+
* omitting absent fields so downstream result shapes stay free of `undefined`
|
|
216
|
+
* keys.
|
|
217
|
+
*/
|
|
218
|
+
function envelopeSideChannels(output: {
|
|
219
|
+
dependencies?: Record<string, string[]>;
|
|
220
|
+
dependenciesComplete?: string[];
|
|
221
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
222
|
+
volatile?: string[];
|
|
223
|
+
}): {
|
|
224
|
+
dependencies?: Record<string, string[]>;
|
|
225
|
+
dependenciesComplete?: string[];
|
|
226
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
227
|
+
volatile?: string[];
|
|
228
|
+
} {
|
|
199
229
|
return {
|
|
200
230
|
...(output.dependencies === undefined
|
|
201
231
|
? {}
|
|
202
232
|
: { dependencies: output.dependencies }),
|
|
203
|
-
|
|
204
|
-
|
|
233
|
+
...(output.dependenciesComplete === undefined
|
|
234
|
+
? {}
|
|
235
|
+
: { dependenciesComplete: output.dependenciesComplete }),
|
|
236
|
+
...(output.graph === undefined ? {} : { graph: output.graph }),
|
|
237
|
+
...(output.volatile === undefined ? {} : { volatile: output.volatile }),
|
|
205
238
|
};
|
|
206
239
|
}
|
|
207
240
|
|
|
@@ -368,24 +401,34 @@ function nativePluginEnv(
|
|
|
368
401
|
* treated as a protocol error and throws with the stderr/stdout context. JSON
|
|
369
402
|
* parse errors are also wrapped with the same context message.
|
|
370
403
|
*
|
|
371
|
-
* The optional `dependencies`
|
|
372
|
-
* `ITtscCompilerTransformation`)
|
|
373
|
-
*
|
|
374
|
-
*
|
|
404
|
+
* The optional `dependencies`, `dependenciesComplete`, `graph`, and `volatile`
|
|
405
|
+
* fields (see `ITtscCompilerTransformation`) are forwarded when well-formed;
|
|
406
|
+
* entries that do not match the expected shape are dropped rather than failing
|
|
407
|
+
* the transform — the fields are advisory invalidation metadata, not output.
|
|
408
|
+
*
|
|
409
|
+
* Dropping a malformed `dependenciesComplete` member is the safe direction on
|
|
410
|
+
* purpose: an unlisted file keeps the sound host-owned bound, so a garbled
|
|
411
|
+
* declaration costs over-invalidation, never a stale output.
|
|
375
412
|
*/
|
|
376
413
|
function parseNativeTransformOutput(
|
|
377
414
|
stdout: string,
|
|
378
415
|
stderr: string,
|
|
379
416
|
): {
|
|
380
417
|
dependencies?: Record<string, string[]>;
|
|
418
|
+
dependenciesComplete?: string[];
|
|
381
419
|
diagnostics: ITtscCompilerDiagnostic[];
|
|
420
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
382
421
|
typescript: Record<string, string>;
|
|
422
|
+
volatile?: string[];
|
|
383
423
|
} {
|
|
384
424
|
try {
|
|
385
425
|
const parsed = JSON.parse(stdout) as {
|
|
386
426
|
dependencies?: Record<string, string[]>;
|
|
427
|
+
dependenciesComplete?: string[];
|
|
387
428
|
diagnostics?: ITtscCompilerDiagnostic[];
|
|
429
|
+
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
388
430
|
typescript?: Record<string, string>;
|
|
431
|
+
volatile?: string[];
|
|
389
432
|
};
|
|
390
433
|
if (!isTextRecord(parsed.typescript)) {
|
|
391
434
|
throw new Error(
|
|
@@ -393,8 +436,14 @@ function parseNativeTransformOutput(
|
|
|
393
436
|
);
|
|
394
437
|
}
|
|
395
438
|
const dependencies = parseDependencyLists(parsed.dependencies);
|
|
439
|
+
const dependenciesComplete = parseFileList(parsed.dependenciesComplete);
|
|
440
|
+
const graph = parseReferenceGraph(parsed.graph);
|
|
441
|
+
const volatile = parseFileList(parsed.volatile);
|
|
396
442
|
return {
|
|
397
443
|
...(dependencies === undefined ? {} : { dependencies }),
|
|
444
|
+
...(dependenciesComplete === undefined ? {} : { dependenciesComplete }),
|
|
445
|
+
...(graph === undefined ? {} : { graph }),
|
|
446
|
+
...(volatile === undefined ? {} : { volatile }),
|
|
398
447
|
diagnostics: Array.isArray(parsed.diagnostics) ? parsed.diagnostics : [],
|
|
399
448
|
typescript: parsed.typescript,
|
|
400
449
|
};
|
|
@@ -434,6 +483,51 @@ function parseDependencyLists(
|
|
|
434
483
|
return Object.keys(output).length === 0 ? undefined : output;
|
|
435
484
|
}
|
|
436
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Normalize the optional `graph` envelope section with the same tolerance as
|
|
488
|
+
* `dependencies`: non-object sections are dropped, edge entries that are not
|
|
489
|
+
* string arrays are dropped, and non-string list members are filtered. A
|
|
490
|
+
* section carrying nothing usable collapses to `undefined`.
|
|
491
|
+
*/
|
|
492
|
+
function parseReferenceGraph(
|
|
493
|
+
value: unknown,
|
|
494
|
+
): ITtscCompilerTransformation.IReferenceGraph | undefined {
|
|
495
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
const section = value as {
|
|
499
|
+
configs?: unknown;
|
|
500
|
+
edges?: unknown;
|
|
501
|
+
globals?: unknown;
|
|
502
|
+
};
|
|
503
|
+
const edges = parseDependencyLists(section.edges) ?? {};
|
|
504
|
+
const globals = parseFileList(section.globals) ?? [];
|
|
505
|
+
const configs = parseFileList(section.configs) ?? [];
|
|
506
|
+
if (
|
|
507
|
+
Object.keys(edges).length === 0 &&
|
|
508
|
+
globals.length === 0 &&
|
|
509
|
+
configs.length === 0
|
|
510
|
+
) {
|
|
511
|
+
return undefined;
|
|
512
|
+
}
|
|
513
|
+
return { configs, edges, globals };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Normalize an optional string-list envelope field (`dependenciesComplete`,
|
|
518
|
+
* `volatile`, and the `globals`/`configs` graph sections), or `undefined` when
|
|
519
|
+
* absent or carrying nothing usable.
|
|
520
|
+
*/
|
|
521
|
+
function parseFileList(value: unknown): string[] | undefined {
|
|
522
|
+
if (!Array.isArray(value)) {
|
|
523
|
+
return undefined;
|
|
524
|
+
}
|
|
525
|
+
const files = value.filter(
|
|
526
|
+
(entry): entry is string => typeof entry === "string" && entry.length !== 0,
|
|
527
|
+
);
|
|
528
|
+
return files.length === 0 ? undefined : files;
|
|
529
|
+
}
|
|
530
|
+
|
|
437
531
|
/** Type guard: true when `value` is a non-null, non-array object of strings. */
|
|
438
532
|
function isTextRecord(value: unknown): value is Record<string, string> {
|
|
439
533
|
return (
|
|
@@ -14,6 +14,42 @@ export type ITtscCompilerTransformation =
|
|
|
14
14
|
| ITtscCompilerTransformation.IException;
|
|
15
15
|
|
|
16
16
|
export namespace ITtscCompilerTransformation {
|
|
17
|
+
/**
|
|
18
|
+
* Host-owned reference graph of the transformed program, mirroring the
|
|
19
|
+
* envelope's optional `graph` section.
|
|
20
|
+
*
|
|
21
|
+
* The graph is the language-semantic input bound of the transform under `tsc
|
|
22
|
+
* --incremental` semantics: any symbol a file can reference is reachable
|
|
23
|
+
* through its import/reference closure or is ambient. Bundler adapters
|
|
24
|
+
* register, per transformed file `F`, the reachability closure of
|
|
25
|
+
* {@link edges} from `F` together with {@link globals} and {@link configs}, so
|
|
26
|
+
* persistent caches and watch graphs invalidate soundly without per-plugin
|
|
27
|
+
* dependency reporting.
|
|
28
|
+
*
|
|
29
|
+
* Keys and values follow the same convention as {@link ISuccess.typescript}:
|
|
30
|
+
* project-relative slash paths, falling back to absolute slash paths outside
|
|
31
|
+
* the project root.
|
|
32
|
+
*/
|
|
33
|
+
export interface IReferenceGraph {
|
|
34
|
+
/**
|
|
35
|
+
* Direct resolved references per file: imports, re-exports, `///
|
|
36
|
+
* <reference>` targets, and type reference directives — type-only edges
|
|
37
|
+
* included. Direct edges only; consumers compute transitive reachability
|
|
38
|
+
* themselves.
|
|
39
|
+
*/
|
|
40
|
+
edges: Record<string, string[]>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Files contributing to the global scope (ambient declaration files, script
|
|
44
|
+
* files, global augmentations, `typeRoots` entries). A change to any of
|
|
45
|
+
* them can affect every file in the program.
|
|
46
|
+
*/
|
|
47
|
+
globals: string[];
|
|
48
|
+
|
|
49
|
+
/** The project tsconfig followed by its `extends` ancestry. */
|
|
50
|
+
configs: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
17
53
|
/** Successful source-to-source transformation result. */
|
|
18
54
|
export interface ISuccess {
|
|
19
55
|
/** Indicates that transformation completed without diagnostics. */
|
|
@@ -44,6 +80,57 @@ export namespace ITtscCompilerTransformation {
|
|
|
44
80
|
* invalidation. ttsc passes the paths through verbatim.
|
|
45
81
|
*/
|
|
46
82
|
dependencies?: Record<string, string[]>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Transformed files (keyed like {@link typescript}) whose
|
|
86
|
+
* {@link dependencies} entry the transform host declares **complete**: every
|
|
87
|
+
* input beyond the file itself and the universal
|
|
88
|
+
* {@link IReferenceGraph.configs} chain is listed there.
|
|
89
|
+
*
|
|
90
|
+
* The declaration narrows invalidation. For a listed file a consumer uses
|
|
91
|
+
* `dependencies[F] ∪ graph.configs` instead of the union with the
|
|
92
|
+
* host-owned `reach(graph.edges, F) ∪ graph.globals` bound, so a change to
|
|
93
|
+
* a file the transform never consulted no longer re-runs it. Unlisted files
|
|
94
|
+
* keep the union, so a mixed envelope composes per file.
|
|
95
|
+
*
|
|
96
|
+
* This is a responsibility transfer, not a hint: an omission makes the
|
|
97
|
+
* consumer serve stale output, and that is a defect of the declaring plugin
|
|
98
|
+
* rather than of the host. Producers list a file only when the reported set
|
|
99
|
+
* is derived from what the transform actually consulted (a Checker-driven
|
|
100
|
+
* generator's per-file consulted-declaration list). When several plugin
|
|
101
|
+
* entries contribute to one file, the envelope's author may list it only if
|
|
102
|
+
* every contributing entry declared its own list complete for it.
|
|
103
|
+
*
|
|
104
|
+
* The config chain a listed file keeps is {@link graph}'s, so a host that
|
|
105
|
+
* declares completeness should stamp {@link graph} too; without it a listed
|
|
106
|
+
* file retains no universal input at all.
|
|
107
|
+
*
|
|
108
|
+
* Optional, and never required for correctness: an envelope without this
|
|
109
|
+
* field keeps the sound host-owned bound. A file that is both listed here
|
|
110
|
+
* and in {@link volatile} keeps the union, since the two claims contradict
|
|
111
|
+
* and the conservative one wins.
|
|
112
|
+
*/
|
|
113
|
+
dependenciesComplete?: string[];
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Host-owned reference graph of the transformed program.
|
|
117
|
+
*
|
|
118
|
+
* Optional: only present when the transform host stamped a `graph` section
|
|
119
|
+
* into its stdout envelope (the built-in native host and the linked-plugin
|
|
120
|
+
* host always do; external sidecars adopt through the driver SDK).
|
|
121
|
+
* Malformed sections are dropped, never fatal — the field is advisory
|
|
122
|
+
* invalidation metadata, not output.
|
|
123
|
+
*/
|
|
124
|
+
graph?: IReferenceGraph;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Transformed files (keyed like {@link typescript}) whose output depends on
|
|
128
|
+
* non-file inputs (environment, time, network) as declared by the transform
|
|
129
|
+
* plugin via the envelope's optional `volatile` list. No file-dependency
|
|
130
|
+
* scheme can represent such inputs, so consumers must exclude these files
|
|
131
|
+
* from caching instead of watching more files.
|
|
132
|
+
*/
|
|
133
|
+
volatile?: string[];
|
|
47
134
|
}
|
|
48
135
|
|
|
49
136
|
/** Source-to-source transformation result that completed with diagnostics. */
|
|
@@ -69,6 +156,26 @@ export namespace ITtscCompilerTransformation {
|
|
|
69
156
|
* transform did not complete its pass.
|
|
70
157
|
*/
|
|
71
158
|
dependencies?: Record<string, string[]>;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Files whose reported dependency list the host declares complete. Same
|
|
162
|
+
* shape and semantics as {@link ISuccess.dependenciesComplete}; may be
|
|
163
|
+
* partial when the transform did not complete its pass.
|
|
164
|
+
*/
|
|
165
|
+
dependenciesComplete?: string[];
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Host-owned reference graph. Same shape and semantics as
|
|
169
|
+
* {@link ISuccess.graph}; may be absent when diagnostics prevented the host
|
|
170
|
+
* from loading the program.
|
|
171
|
+
*/
|
|
172
|
+
graph?: IReferenceGraph;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Volatile transformed files. Same shape and semantics as
|
|
176
|
+
* {@link ISuccess.volatile}.
|
|
177
|
+
*/
|
|
178
|
+
volatile?: string[];
|
|
72
179
|
}
|
|
73
180
|
|
|
74
181
|
/** Unexpected host-level error during transformation. */
|
|
@@ -669,7 +669,6 @@
|
|
|
669
669
|
"FUNC|core|ResolveConfigFileNameOfProjectReference",
|
|
670
670
|
"FUNC|core|ShouldRewriteModuleSpecifier",
|
|
671
671
|
"FUNC|core|UTF16Len",
|
|
672
|
-
"FUNC|core|Version",
|
|
673
672
|
"FUNC|core|VersionMajorMinor",
|
|
674
673
|
"FUNC|core|WithRequestID",
|
|
675
674
|
"FUNC|diagnosticwriter|FormatDiagnosticWithColorAndContext",
|
|
@@ -678,6 +677,8 @@
|
|
|
678
677
|
"FUNC|diagnosticwriter|TryClearScreen",
|
|
679
678
|
"FUNC|diagnosticwriter|WriteFormatDiagnostic",
|
|
680
679
|
"FUNC|diagnosticwriter|WriteFormatDiagnostics",
|
|
680
|
+
"FUNC|execute/incremental|ComputeHash",
|
|
681
|
+
"FUNC|execute/incremental|GetMTime",
|
|
681
682
|
"FUNC|printer|FormatGeneratedName",
|
|
682
683
|
"FUNC|printer|GetDefaultIndentSize",
|
|
683
684
|
"FUNC|printer|GetEmitContext",
|
package/tools/shim_audit/main.go
CHANGED
|
@@ -90,6 +90,19 @@ var shimDirs = map[string]string{
|
|
|
90
90
|
"vfs/osvfs": "vfs/osvfs",
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// auditOnlyInternal lists upstream internal packages that shim sources
|
|
94
|
+
// reference — go:linkname targets and blank imports that compile them into
|
|
95
|
+
// consumer binaries — without owning a shim directory of their own. They are
|
|
96
|
+
// loaded for type information so the shim import scan can resolve them; their
|
|
97
|
+
// exported surface then enters the ratcheted reachable-gap scan like any other
|
|
98
|
+
// loaded package, so unneeded helpers are accepted into baseline.json rather
|
|
99
|
+
// than silently skipped.
|
|
100
|
+
var auditOnlyInternal = []string{
|
|
101
|
+
// shim/compiler/incremental.go linknames the incremental engine's
|
|
102
|
+
// reference-graph functions (getReferencedFiles, fileAffectsGlobalScope).
|
|
103
|
+
"execute/incremental",
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
// reachable holds, per upstream package suffix, the set of upstream symbol names
|
|
94
107
|
// the shim makes nameable (selector references + linkname targets).
|
|
95
108
|
type reachable map[string]map[string]bool
|
|
@@ -525,6 +538,10 @@ func loadInner(anchorDir string) (map[string]*packages.Package, error) {
|
|
|
525
538
|
full = append(full, internalPrefix+suffix)
|
|
526
539
|
expected[suffix] = true
|
|
527
540
|
}
|
|
541
|
+
for _, suffix := range auditOnlyInternal {
|
|
542
|
+
full = append(full, internalPrefix+suffix)
|
|
543
|
+
expected[suffix] = true
|
|
544
|
+
}
|
|
528
545
|
loaded, err := packages.Load(&packages.Config{
|
|
529
546
|
Dir: anchorDir,
|
|
530
547
|
Mode: packages.LoadTypes | packages.NeedDeps | packages.NeedImports,
|