ttsc 0.26.2 → 0.27.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/README.md +9 -0
- package/cmd/ttsc/api_transform.go +17 -3
- package/cmd/ttscgraph/auxiliary_identity_state_tracks_retargets_not_source_contents_test.go +85 -0
- package/cmd/ttscgraph/disk_realpath_other.go +16 -0
- package/cmd/ttscgraph/disk_realpath_windows.go +56 -0
- package/cmd/ttscgraph/serve.go +104 -35
- package/driver/emit_plugin.go +20 -3
- package/driver/graph.go +64 -11
- package/driver/input_observation.go +273 -0
- package/driver/input_observation_test.go +149 -0
- package/driver/plugins.go +384 -4
- package/driver/program.go +54 -1
- package/driver/resolution_candidates.go +13 -5
- package/driver/rewrite.go +45 -2
- package/lib/TtscCompiler.d.ts +12 -11
- package/lib/TtscCompiler.js +44 -29
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/captureProcessOutput.js +2 -2
- package/lib/compiler/internal/captureProcessOutput.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +4 -3
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +2 -0
- package/lib/compiler/internal/resolveEmittedJavaScript.js +3 -0
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +1 -1
- package/lib/compiler/internal/runBuild.js +80 -34
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +2 -2
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +28 -0
- package/lib/compiler/internal/sharedHostHelpers.js +39 -0
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +12 -4
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.d.ts +3 -0
- package/lib/compiler/internal/transformProjectInMemory.js +239 -35
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/schema.js +12 -5
- package/lib/flags/schema.js.map +1 -1
- package/lib/internal/createCanonicalTempDirectory.d.ts +17 -0
- package/lib/internal/createCanonicalTempDirectory.js +42 -0
- package/lib/internal/createCanonicalTempDirectory.js.map +1 -0
- package/lib/internal/projectInputPathIdentity.d.ts +3 -0
- package/lib/internal/projectInputPathIdentity.js +11 -5
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/internal/resolveNodeBinary.d.ts +14 -0
- package/lib/internal/resolveNodeBinary.js +150 -0
- package/lib/internal/resolveNodeBinary.js.map +1 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.d.ts +15 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.js +61 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.js.map +1 -0
- package/lib/launcher/internal/prepareExecution.d.ts +3 -0
- package/lib/launcher/internal/prepareExecution.js +42 -14
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +25 -13
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +18 -3
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +2 -0
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +37 -1
- package/lib/launcher/internal/runtimeHooks.js +653 -62
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +62 -8
- package/lib/plugin/internal/buildSourcePlugin.js +754 -72
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +17 -5
- package/lib/plugin/internal/loadProjectPlugins.js +1339 -77
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/register.d.ts +1 -0
- package/lib/register.js +46 -0
- package/lib/register.js.map +1 -0
- package/lib/structures/ITtscCompilerContext.d.ts +6 -3
- package/lib/structures/ITtscCompilerTransformation.d.ts +31 -0
- package/lib/structures/ITtscPlugin.d.ts +33 -0
- package/package.json +16 -9
- package/shim/checker/go.mod +9 -0
- package/shim/checker/go.sum +22 -0
- package/shim/checker/shim.go +75 -0
- package/shim/compiler/incremental.go +45 -8
- package/src/TtscCompiler.ts +55 -33
- package/src/compiler/internal/captureProcessOutput.ts +3 -2
- package/src/compiler/internal/compileProjectInMemory.ts +4 -3
- package/src/compiler/internal/resolveEmittedJavaScript.ts +6 -0
- package/src/compiler/internal/runBuild.ts +102 -39
- package/src/compiler/internal/runSingleFileEmit.ts +2 -2
- package/src/compiler/internal/sharedHostHelpers.ts +44 -0
- package/src/compiler/internal/startResidentTransform.ts +12 -3
- package/src/compiler/internal/transformProjectInMemory.ts +410 -39
- package/src/flags/schema.ts +12 -5
- package/src/internal/createCanonicalTempDirectory.ts +53 -0
- package/src/internal/projectInputPathIdentity.ts +17 -4
- package/src/internal/resolveNodeBinary.ts +173 -0
- package/src/internal/resolveSafeCacheCleanupTargets.ts +85 -0
- package/src/launcher/internal/prepareExecution.ts +50 -14
- package/src/launcher/internal/runTtsc.ts +24 -13
- package/src/launcher/internal/runTtscserver.ts +18 -3
- package/src/launcher/internal/runTtsx.ts +2 -0
- package/src/launcher/internal/runtimeHooks.ts +823 -66
- package/src/plugin/internal/buildSourcePlugin.ts +984 -79
- package/src/plugin/internal/loadProjectPlugins.ts +1718 -104
- package/src/register.ts +47 -0
- package/src/structures/ITtscCompilerContext.ts +6 -3
- package/src/structures/ITtscCompilerTransformation.ts +39 -0
- package/src/structures/ITtscPlugin.ts +36 -0
- package/tools/shim_audit/baseline.json +0 -1
- package/utility/host.go +9 -3
- package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +0 -5
- package/lib/internal/assertSafeExplicitCacheDirectory.js +0 -26
- package/lib/internal/assertSafeExplicitCacheDirectory.js.map +0 -1
- package/src/internal/assertSafeExplicitCacheDirectory.ts +0 -29
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
// gen_shims:hand-maintained
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Parts of tsgo's own incremental engine (`tsc --incremental` semantics),
|
|
4
|
+
// exposed from internal/execute/incremental for the two things a ttsc host
|
|
5
|
+
// needs from it:
|
|
6
|
+
//
|
|
7
|
+
// - the reference graph, so transform envelopes can carry the same
|
|
8
|
+
// language-semantic input bound the compiler itself uses for invalidation:
|
|
9
|
+
// per-file direct resolved references and the files that contribute to the
|
|
10
|
+
// global scope;
|
|
11
|
+
// - the build-information emit, so a host that constructs its Program
|
|
12
|
+
// in-process still writes the `.tsbuildinfo` an `incremental` or
|
|
13
|
+
// `composite` project asked for.
|
|
8
14
|
package compiler
|
|
9
15
|
|
|
10
16
|
import (
|
|
17
|
+
"context"
|
|
11
18
|
_ "unsafe"
|
|
12
19
|
|
|
13
20
|
innerast "github.com/microsoft/typescript-go/internal/ast"
|
|
@@ -15,11 +22,41 @@ import (
|
|
|
15
22
|
innercompiler "github.com/microsoft/typescript-go/internal/compiler"
|
|
16
23
|
"github.com/microsoft/typescript-go/internal/tspath"
|
|
17
24
|
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
25
|
+
// Imported for EmitFreshWithBuildInfo below, and for the linknamed symbols
|
|
26
|
+
// further down: compiling the package into every shim consumer is what makes
|
|
27
|
+
// those references resolve.
|
|
28
|
+
"github.com/microsoft/typescript-go/internal/execute/incremental"
|
|
21
29
|
)
|
|
22
30
|
|
|
31
|
+
// EmitFreshWithBuildInfo runs a full emit through tsgo's own incremental
|
|
32
|
+
// program, so an `incremental` or `composite` project gets the `.tsbuildinfo`
|
|
33
|
+
// tsgo would have written, in the exact format and location
|
|
34
|
+
// `outputpaths.GetBuildInfoFileName` resolves from the compiler options.
|
|
35
|
+
//
|
|
36
|
+
// This is the emit half of `tsc.go::performIncrementalCompilation`, which the
|
|
37
|
+
// tsgo CLI takes whenever `CompilerOptions.IsIncremental()`. A host that builds
|
|
38
|
+
// its Program in-process (ttsc's driver, and therefore every plugin sidecar
|
|
39
|
+
// emitting through it) never reaches that CLI path, so without this the build
|
|
40
|
+
// information is silently dropped even though the options parsed cleanly.
|
|
41
|
+
//
|
|
42
|
+
// "Fresh" is the load-bearing word: no previous snapshot is supplied, so
|
|
43
|
+
// `programToSnapshot` marks every file changed and this emits the whole program
|
|
44
|
+
// exactly as `Program.Emit` does, then writes the build info. A ttsc plugin's
|
|
45
|
+
// output is not a pure function of the source text a build info records — it
|
|
46
|
+
// also depends on the plugin binary, its config file, and its contributors —
|
|
47
|
+
// so reusing a previous snapshot to skip a file would serve stale transformed
|
|
48
|
+
// output. Producing the record is sound; consuming it needs plugin identity in
|
|
49
|
+
// the invalidation key first.
|
|
50
|
+
func EmitFreshWithBuildInfo(ctx context.Context, program *Program, options EmitOptions) *EmitResult {
|
|
51
|
+
incrementalProgram := incremental.NewProgram(
|
|
52
|
+
program,
|
|
53
|
+
nil,
|
|
54
|
+
incremental.CreateHost(program.Host()),
|
|
55
|
+
false,
|
|
56
|
+
)
|
|
57
|
+
return incrementalProgram.Emit(ctx, options)
|
|
58
|
+
}
|
|
59
|
+
|
|
23
60
|
//go:linkname incrementalGetReferencedFiles github.com/microsoft/typescript-go/internal/execute/incremental.getReferencedFiles
|
|
24
61
|
func incrementalGetReferencedFiles(program *innercompiler.Program, file *innerast.SourceFile) *collections.Set[tspath.Path]
|
|
25
62
|
|
package/src/TtscCompiler.ts
CHANGED
|
@@ -6,7 +6,10 @@ import { resolveProjectConfig } from "./compiler/internal/project/resolveProject
|
|
|
6
6
|
import { resolveBinary } from "./compiler/internal/resolveBinary";
|
|
7
7
|
import { createProcessDiagnostic } from "./compiler/internal/runBuild";
|
|
8
8
|
import { transformProjectInMemory } from "./compiler/internal/transformProjectInMemory";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
type SafeCacheCleanupTarget,
|
|
11
|
+
resolveSafeCacheCleanupTargets,
|
|
12
|
+
} from "./internal/resolveSafeCacheCleanupTargets";
|
|
10
13
|
import { resolveCleanTargets } from "./plugin/internal/buildSourcePlugin";
|
|
11
14
|
import { loadProjectPlugins } from "./plugin/internal/loadProjectPlugins";
|
|
12
15
|
import type { ITtscCompilerContext } from "./structures/ITtscCompilerContext";
|
|
@@ -86,15 +89,16 @@ export class TtscCompiler {
|
|
|
86
89
|
/**
|
|
87
90
|
* Remove compiled cache artifacts for this compiler instance.
|
|
88
91
|
*
|
|
89
|
-
* Removes
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* project-local caches. A user-provided `GOCACHE` is never removed.
|
|
92
|
+
* Removes an explicit `cacheDir` wholesale. Otherwise removes the plugin
|
|
93
|
+
* binary subdirectory, a safely identified ttsc-owned Go build cache
|
|
94
|
+
* (including an external `TTSC_GO_CACHE_DIR`), and the two legacy
|
|
95
|
+
* project-local caches. A user-provided `GOCACHE` is never removed. Every
|
|
96
|
+
* resolved deletion target is validated before the first removal. The cache
|
|
93
97
|
* location comes from this instance's `cacheDir` and environment
|
|
94
98
|
* (`TTSC_CACHE_DIR` / `TTSC_GO_CACHE_DIR`), defaulting to
|
|
95
|
-
* `<workspaceRoot>/node_modules/.cache/ttsc`.
|
|
96
|
-
*
|
|
97
|
-
*
|
|
99
|
+
* `<workspaceRoot>/node_modules/.cache/ttsc`. Any resolved cache target that
|
|
100
|
+
* equals or contains the project, or names a filesystem root, is rejected
|
|
101
|
+
* before any directory is removed.
|
|
98
102
|
*
|
|
99
103
|
* @returns Cache directories that were removed.
|
|
100
104
|
*/
|
|
@@ -105,31 +109,35 @@ export class TtscCompiler {
|
|
|
105
109
|
path.join(projectRoot, ".ttsc"),
|
|
106
110
|
];
|
|
107
111
|
const explicitCacheDir = this.resolveCacheDir();
|
|
112
|
+
let targets: string[];
|
|
108
113
|
if (explicitCacheDir !== undefined) {
|
|
109
|
-
assertSafeExplicitCacheDirectory(projectRoot, explicitCacheDir);
|
|
110
114
|
// An explicit constructor `cacheDir` names the cache directory for this
|
|
111
115
|
// instance — exactly like `ttsc clean --cache-dir X` on the CLI — so
|
|
112
116
|
// remove it wholesale plus the legacy project-local caches. This keeps
|
|
113
117
|
// the programmatic and CLI clean contracts identical for an explicit
|
|
114
118
|
// cache dir.
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
resolveCleanTargets(
|
|
119
|
+
targets = [explicitCacheDir, ...legacyTargets];
|
|
120
|
+
} else {
|
|
121
|
+
// Default / `context.env.TTSC_CACHE_DIR`: resolve the cache root and the Go
|
|
122
|
+
// build cache (`TTSC_GO_CACHE_DIR`) from this instance's effective
|
|
123
|
+
// environment — the same `{ ...process.env, ...context.env }` that
|
|
124
|
+
// prepare()/compile()/transform() build with — then remove only the
|
|
125
|
+
// ttsc-owned subdirectories, so a possibly-shared root is never deleted and
|
|
126
|
+
// a user-provided `GOCACHE` is never touched. Using the effective env (not
|
|
127
|
+
// ambient `process.env`) makes clean() remove exactly the artifacts this
|
|
128
|
+
// instance owns, including a `TTSC_GO_CACHE_DIR` supplied only in
|
|
129
|
+
// `context.env`.
|
|
130
|
+
targets = resolveCleanTargets(
|
|
128
131
|
projectRoot,
|
|
129
132
|
this.resolvePluginCacheDir(),
|
|
130
133
|
this.resolveEffectiveEnv(),
|
|
131
|
-
)
|
|
132
|
-
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
// Validate the complete deletion set before removing the first directory.
|
|
137
|
+
// This includes an environment-selected TTSC_GO_CACHE_DIR and project-local
|
|
138
|
+
// legacy locations, not only an explicit constructor cacheDir.
|
|
139
|
+
const safeTargets = resolveSafeCacheCleanupTargets(projectRoot, targets);
|
|
140
|
+
return removeExistingDirectories(safeTargets);
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
/**
|
|
@@ -234,24 +242,29 @@ export class TtscCompiler {
|
|
|
234
242
|
* The effective environment for this instance's source-plugin builds and
|
|
235
243
|
* clean targets: `context.env` merged over `process.env`, matching the
|
|
236
244
|
* documented {@link ITtscCompilerContext.env} contract that child compiler,
|
|
237
|
-
* native-plugin,
|
|
238
|
-
* fresh object so callers never mutate the shared
|
|
239
|
-
* `context.env` was supplied this is a plain copy of
|
|
240
|
-
* default behavior is unchanged.
|
|
245
|
+
* native-plugin, native-host, and isolated descriptor processes already
|
|
246
|
+
* receive. Returned as a fresh object so callers never mutate the shared
|
|
247
|
+
* `process.env`; when no `context.env` was supplied this is a plain copy of
|
|
248
|
+
* `process.env`, so CLI / default behavior is unchanged.
|
|
241
249
|
*/
|
|
242
250
|
private resolveEffectiveEnv(): NodeJS.ProcessEnv {
|
|
243
251
|
return { ...process.env, ...this.context.env };
|
|
244
252
|
}
|
|
245
253
|
}
|
|
246
254
|
|
|
247
|
-
function removeExistingDirectories(
|
|
255
|
+
function removeExistingDirectories(
|
|
256
|
+
directories: readonly SafeCacheCleanupTarget[],
|
|
257
|
+
): string[] {
|
|
248
258
|
const removed: string[] = [];
|
|
249
|
-
|
|
250
|
-
|
|
259
|
+
const visited = new Set<string>();
|
|
260
|
+
for (const directory of directories) {
|
|
261
|
+
if (visited.has(directory.path)) continue;
|
|
262
|
+
visited.add(directory.path);
|
|
263
|
+
if (!directory.exists || !fs.existsSync(directory.path)) {
|
|
251
264
|
continue;
|
|
252
265
|
}
|
|
253
|
-
fs.rmSync(directory, { recursive: true, force: true });
|
|
254
|
-
removed.push(directory);
|
|
266
|
+
fs.rmSync(directory.path, { recursive: true, force: true });
|
|
267
|
+
removed.push(directory.requestedPath);
|
|
255
268
|
}
|
|
256
269
|
return removed;
|
|
257
270
|
}
|
|
@@ -265,6 +278,9 @@ interface ProjectTransformation {
|
|
|
265
278
|
dependencies?: Record<string, string[]>;
|
|
266
279
|
dependenciesComplete?: string[];
|
|
267
280
|
graph?: ITtscCompilerTransformation.IReferenceGraph;
|
|
281
|
+
hostInputHashes?: Record<string, string | null>;
|
|
282
|
+
hostInputRealpaths?: Record<string, string | null>;
|
|
283
|
+
hostInputs?: string[];
|
|
268
284
|
result: TtscBuildResult;
|
|
269
285
|
typescript: Record<string, string>;
|
|
270
286
|
volatile?: string[];
|
|
@@ -382,6 +398,9 @@ function toCompilerTransformation(
|
|
|
382
398
|
dependencies,
|
|
383
399
|
dependenciesComplete,
|
|
384
400
|
graph,
|
|
401
|
+
hostInputHashes,
|
|
402
|
+
hostInputRealpaths,
|
|
403
|
+
hostInputs,
|
|
385
404
|
result,
|
|
386
405
|
typescript,
|
|
387
406
|
volatile,
|
|
@@ -390,6 +409,9 @@ function toCompilerTransformation(
|
|
|
390
409
|
...(dependencies === undefined ? {} : { dependencies }),
|
|
391
410
|
...(dependenciesComplete === undefined ? {} : { dependenciesComplete }),
|
|
392
411
|
...(graph === undefined ? {} : { graph }),
|
|
412
|
+
...(hostInputHashes === undefined ? {} : { hostInputHashes }),
|
|
413
|
+
...(hostInputRealpaths === undefined ? {} : { hostInputRealpaths }),
|
|
414
|
+
...(hostInputs === undefined ? {} : { hostInputs }),
|
|
393
415
|
...(volatile === undefined ? {} : { volatile }),
|
|
394
416
|
};
|
|
395
417
|
if (result.status === 0 && !hasErrorDiagnostics(result.diagnostics)) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
|
|
4
|
+
import { createCanonicalTempDirectory } from "../../internal/createCanonicalTempDirectory";
|
|
5
|
+
|
|
5
6
|
export interface CapturedProcessOutput {
|
|
6
7
|
/** Close the descriptors and remove the backing files. */
|
|
7
8
|
dispose(): void;
|
|
@@ -32,7 +33,7 @@ export interface CapturedProcessOutput {
|
|
|
32
33
|
* bytes.
|
|
33
34
|
*/
|
|
34
35
|
export function captureProcessOutput(): CapturedProcessOutput {
|
|
35
|
-
const directory =
|
|
36
|
+
const directory = createCanonicalTempDirectory("ttsc-spawn-");
|
|
36
37
|
const stdoutPath = path.join(directory, "stdout");
|
|
37
38
|
const stderrPath = path.join(directory, "stderr");
|
|
38
39
|
const stdoutFd = fs.openSync(stdoutPath, "w+");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
|
|
4
|
+
import { createCanonicalTempDirectory } from "../../internal/createCanonicalTempDirectory";
|
|
5
5
|
import { hasProjectPluginEntries } from "../../plugin/internal/loadProjectPlugins";
|
|
6
6
|
import type { ITtscCompilerContext } from "../../structures/ITtscCompilerContext";
|
|
7
7
|
import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiagnostic";
|
|
@@ -11,6 +11,7 @@ import { buildNativeCompiler } from "./buildNativeCompiler";
|
|
|
11
11
|
import { isOutsideRelativePath, packageRootDir } from "./paths";
|
|
12
12
|
import { readProjectConfig } from "./project/readProjectConfig";
|
|
13
13
|
import { runBuild } from "./runBuild";
|
|
14
|
+
import { inheritedSidecarEnv } from "./sharedHostHelpers";
|
|
14
15
|
import { outputText, spawnNative } from "./spawnNative";
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -50,7 +51,7 @@ export function compileProjectInMemory(options: ITtscCompilerContext): {
|
|
|
50
51
|
["api-compile", "--cwd", project.root, "--tsconfig", tsconfig],
|
|
51
52
|
{
|
|
52
53
|
cwd: project.root,
|
|
53
|
-
env:
|
|
54
|
+
env: inheritedSidecarEnv(options.env),
|
|
54
55
|
},
|
|
55
56
|
);
|
|
56
57
|
if (res.error) {
|
|
@@ -110,7 +111,7 @@ function compileProjectWithPlugins(
|
|
|
110
111
|
output: Record<string, string>;
|
|
111
112
|
result: TtscBuildResult;
|
|
112
113
|
} {
|
|
113
|
-
const tempRoot =
|
|
114
|
+
const tempRoot = createCanonicalTempDirectory("ttsc-api-output-");
|
|
114
115
|
const tempOutDir = path.join(tempRoot, "out");
|
|
115
116
|
try {
|
|
116
117
|
const result = runBuild({
|
|
@@ -20,6 +20,8 @@ import { isOutsideRelativePath } from "./paths";
|
|
|
20
20
|
* Returns `null` when no matching output file is found on disk.
|
|
21
21
|
*/
|
|
22
22
|
export function resolveEmittedJavaScript(options: {
|
|
23
|
+
/** Skip trailing-stem recovery when exact source ownership is required. */
|
|
24
|
+
allowStemFallback?: boolean;
|
|
23
25
|
/** Pre-computed list of emitted paths; when absent `outDir` is scanned. */
|
|
24
26
|
emittedFiles?: readonly string[];
|
|
25
27
|
outDir: string;
|
|
@@ -45,6 +47,10 @@ export function resolveEmittedJavaScript(options: {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
if (options.allowStemFallback === false) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
// Score the pre-computed emit list first (cheap). When it yields nothing —
|
|
49
55
|
// because the list is incomplete (a native transform host such as typia emits
|
|
50
56
|
// without printing the `--listEmittedFiles` lines) or because the emit landed
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ProjectInputPathIdentityContext,
|
|
7
7
|
createProjectInputPathIdentityContext,
|
|
8
8
|
} from "../../internal/projectInputPathIdentity";
|
|
9
|
+
import { resolveNodeBinary } from "../../internal/resolveNodeBinary";
|
|
9
10
|
import {
|
|
10
11
|
hasProjectPluginEntries,
|
|
11
12
|
loadProjectPlugins,
|
|
@@ -26,7 +27,10 @@ import {
|
|
|
26
27
|
import { resolveBinary } from "./resolveBinary";
|
|
27
28
|
import { resolveTsgo } from "./resolveTsgo";
|
|
28
29
|
import {
|
|
30
|
+
TSGO_ARGS_ENV,
|
|
29
31
|
assertSharedHostCompatibility,
|
|
32
|
+
clearInheritedTsgoArgs,
|
|
33
|
+
inheritedSidecarEnv,
|
|
30
34
|
linkedTransformPlugins,
|
|
31
35
|
resolvePluginConfigDir,
|
|
32
36
|
selectSharedHostPlugin,
|
|
@@ -101,13 +105,18 @@ type BuildTiming = {
|
|
|
101
105
|
* `TTSC_NODE_BINARY` so child processes can re-invoke the same Node.js binary
|
|
102
106
|
* without searching `PATH`.
|
|
103
107
|
*/
|
|
104
|
-
function mergeEnv(
|
|
105
|
-
|
|
108
|
+
function mergeEnv(
|
|
109
|
+
extra?: NodeJS.ProcessEnv,
|
|
110
|
+
cwd: string = process.cwd(),
|
|
111
|
+
): NodeJS.ProcessEnv {
|
|
112
|
+
const env: NodeJS.ProcessEnv = {
|
|
106
113
|
...process.env,
|
|
107
|
-
|
|
114
|
+
...extra,
|
|
108
115
|
};
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
const node = resolveNodeBinary(env, cwd);
|
|
117
|
+
if (node === undefined) delete env.TTSC_NODE_BINARY;
|
|
118
|
+
else env.TTSC_NODE_BINARY = node;
|
|
119
|
+
return env;
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
function createBuildTiming(options: TtscCommonOptions): BuildTiming {
|
|
@@ -198,17 +207,29 @@ function nativePluginEnv(
|
|
|
198
207
|
extra: NodeJS.ProcessEnv | undefined,
|
|
199
208
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
200
209
|
plugin?: ITtscLoadedNativePlugin,
|
|
210
|
+
tsgoArgs?: string,
|
|
201
211
|
): NodeJS.ProcessEnv {
|
|
202
|
-
const env = mergeEnv(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
const env = mergeEnv(
|
|
213
|
+
{
|
|
214
|
+
...(execution.pluginConfigDir === undefined
|
|
215
|
+
? {}
|
|
216
|
+
: { TTSC_PLUGIN_CONFIG_DIR: execution.pluginConfigDir }),
|
|
217
|
+
TTSC_TSGO_BINARY: process.env.TTSC_TSGO_BINARY ?? execution.tsgo.binary,
|
|
218
|
+
TTSC_TTSX_BINARY:
|
|
219
|
+
process.env.TTSC_TTSX_BINARY ??
|
|
220
|
+
path.join(__dirname, "..", "..", "launcher", "ttsx.js"),
|
|
221
|
+
...extra,
|
|
222
|
+
},
|
|
223
|
+
execution.projectRoot,
|
|
224
|
+
);
|
|
225
|
+
// Forwarded tsgo argv is per-invocation state this host owns, exactly like
|
|
226
|
+
// the config anchor below: publish this run's payload, or drop whatever an
|
|
227
|
+
// ancestor ttsc process left behind when this lane forwards nothing.
|
|
228
|
+
if (tsgoArgs !== undefined) {
|
|
229
|
+
env[TSGO_ARGS_ENV] = tsgoArgs;
|
|
230
|
+
} else {
|
|
231
|
+
clearInheritedTsgoArgs(env, extra);
|
|
232
|
+
}
|
|
212
233
|
// The anchor is per-invocation state owned by this host: when this run
|
|
213
234
|
// declared none (and the caller's env does not name one), drop any value
|
|
214
235
|
// inherited from an ancestor ttsc process so a nested build never
|
|
@@ -391,8 +412,11 @@ export class ResidentCheckWatchSession {
|
|
|
391
412
|
stderr: "",
|
|
392
413
|
stdout: "",
|
|
393
414
|
};
|
|
394
|
-
const
|
|
395
|
-
|
|
415
|
+
const tsgoArgs = createNativeTsgoArgs(options);
|
|
416
|
+
const checks = planResidentCheckEntries(
|
|
417
|
+
execution.nativePlugins,
|
|
418
|
+
(plugin) => createNativeCheckArgs(execution, options, plugin),
|
|
419
|
+
tsgoArgs,
|
|
396
420
|
);
|
|
397
421
|
const request = residentCheckRequest(change, execution.projectRoot);
|
|
398
422
|
// Buffer the cycle for every resident plugin before running any of them.
|
|
@@ -411,6 +435,7 @@ export class ResidentCheckWatchSession {
|
|
|
411
435
|
"ttsc.check",
|
|
412
436
|
timing,
|
|
413
437
|
`ttsc check plugin ${plugin.name} time`,
|
|
438
|
+
tsgoArgs,
|
|
414
439
|
);
|
|
415
440
|
} else {
|
|
416
441
|
const startedAt = process.hrtime.bigint();
|
|
@@ -420,7 +445,7 @@ export class ResidentCheckWatchSession {
|
|
|
420
445
|
args: ["check-serve", ...args.slice(1)],
|
|
421
446
|
binary: plugin.binary,
|
|
422
447
|
cwd: execution.projectRoot,
|
|
423
|
-
env: nativePluginEnv(options.env, execution, plugin),
|
|
448
|
+
env: nativePluginEnv(options.env, execution, plugin, tsgoArgs),
|
|
424
449
|
});
|
|
425
450
|
this.processes.set(key, resident);
|
|
426
451
|
}
|
|
@@ -506,8 +531,9 @@ function residentCheckExecutionIsCompatible(
|
|
|
506
531
|
function residentCheckProcessKey(
|
|
507
532
|
plugin: ITtscLoadedNativePlugin,
|
|
508
533
|
args: readonly string[],
|
|
534
|
+
tsgoArgs?: string,
|
|
509
535
|
): string {
|
|
510
|
-
return `${plugin.binary}\0${plugin.name}\0${JSON.stringify(args)}`;
|
|
536
|
+
return `${plugin.binary}\0${plugin.name}\0${JSON.stringify(args)}\0${tsgoArgs ?? ""}`;
|
|
511
537
|
}
|
|
512
538
|
|
|
513
539
|
export type ResidentCheckEntryPlan = {
|
|
@@ -524,6 +550,11 @@ export type ResidentCheckEntryPlan = {
|
|
|
524
550
|
export function planResidentCheckEntries(
|
|
525
551
|
plugins: readonly ITtscLoadedNativePlugin[],
|
|
526
552
|
createArgs: (plugin: ITtscLoadedNativePlugin) => string[],
|
|
553
|
+
// Part of the process identity even though it never appears in argv: the
|
|
554
|
+
// forwarded tsgo payload rides the environment, and a resident process holds
|
|
555
|
+
// the compiler options it was started with. Two cycles that forward different
|
|
556
|
+
// flags must not share one warm Program.
|
|
557
|
+
tsgoArgs?: string,
|
|
527
558
|
): ResidentCheckEntryPlan[] {
|
|
528
559
|
return plugins
|
|
529
560
|
.filter((candidate) => candidate.stage === "check")
|
|
@@ -534,7 +565,7 @@ export function planResidentCheckEntries(
|
|
|
534
565
|
entryIndex,
|
|
535
566
|
key:
|
|
536
567
|
plugin.capabilities?.residentCheck === true
|
|
537
|
-
? residentCheckProcessKey(plugin, args)
|
|
568
|
+
? residentCheckProcessKey(plugin, args, tsgoArgs)
|
|
538
569
|
: undefined,
|
|
539
570
|
plugin,
|
|
540
571
|
};
|
|
@@ -830,7 +861,7 @@ function runProjectFreeTerminalFlag(
|
|
|
830
861
|
const tsgo = resolveTsgo({ ...options, cwd });
|
|
831
862
|
const res = spawnNative(tsgo.binary, [...(options.passthrough ?? [])], {
|
|
832
863
|
cwd,
|
|
833
|
-
env: mergeEnv(options.env),
|
|
864
|
+
env: mergeEnv(options.env, cwd),
|
|
834
865
|
encoding: "utf8",
|
|
835
866
|
});
|
|
836
867
|
if (res.error) {
|
|
@@ -1126,6 +1157,7 @@ function buildWithNativeCompilerPlugins(
|
|
|
1126
1157
|
"ttsc.build",
|
|
1127
1158
|
timing,
|
|
1128
1159
|
transformHostTimingLabel(plugins),
|
|
1160
|
+
createNativeBuildTsgoArgs(execution, options),
|
|
1129
1161
|
);
|
|
1130
1162
|
}
|
|
1131
1163
|
|
|
@@ -1150,7 +1182,7 @@ function runTsgo(
|
|
|
1150
1182
|
],
|
|
1151
1183
|
{
|
|
1152
1184
|
cwd: execution.projectRoot,
|
|
1153
|
-
env: mergeEnv(options.env),
|
|
1185
|
+
env: mergeEnv(options.env, execution.projectRoot),
|
|
1154
1186
|
encoding: "utf8",
|
|
1155
1187
|
},
|
|
1156
1188
|
);
|
|
@@ -1184,7 +1216,7 @@ function runTsgoBuild(
|
|
|
1184
1216
|
): TtscBuildResult {
|
|
1185
1217
|
const res = spawnNative(execution.tsgo.binary, args, {
|
|
1186
1218
|
cwd: execution.projectRoot,
|
|
1187
|
-
env: mergeEnv(options.env),
|
|
1219
|
+
env: mergeEnv(options.env, execution.projectRoot),
|
|
1188
1220
|
encoding: "utf8",
|
|
1189
1221
|
});
|
|
1190
1222
|
if (res.error) {
|
|
@@ -1401,15 +1433,23 @@ function createNativeBuildArgs(
|
|
|
1401
1433
|
args.push("--quiet");
|
|
1402
1434
|
}
|
|
1403
1435
|
}
|
|
1404
|
-
// The sidecar builds its Program in-process, so the pinned `rootDir` has to
|
|
1405
|
-
// travel the same channel every other tsgo option takes to it — the host flag
|
|
1406
|
-
// set does not declare `--rootDir`, and `filterHostArgs` would strip it.
|
|
1407
|
-
args.push(
|
|
1408
|
-
...createNativeTsgoArgs(options, pinnedRootDirArgs(execution, options)),
|
|
1409
|
-
);
|
|
1410
1436
|
return args;
|
|
1411
1437
|
}
|
|
1412
1438
|
|
|
1439
|
+
/**
|
|
1440
|
+
* The forwarded-tsgo payload for a native `build`/`check` emit invocation.
|
|
1441
|
+
*
|
|
1442
|
+
* The sidecar builds its Program in-process, so the pinned `rootDir` has to
|
|
1443
|
+
* travel the same channel every other tsgo option takes to it — the host flag
|
|
1444
|
+
* set does not declare `--rootDir`, and `filterHostArgs` would strip it.
|
|
1445
|
+
*/
|
|
1446
|
+
function createNativeBuildTsgoArgs(
|
|
1447
|
+
execution: ReturnType<typeof resolveExecutionContext>,
|
|
1448
|
+
options: RunBuildOptions,
|
|
1449
|
+
): string | undefined {
|
|
1450
|
+
return createNativeTsgoArgs(options, pinnedRootDirArgs(execution, options));
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1413
1453
|
/** Build the argument list for a native plugin check/fix/format invocation. */
|
|
1414
1454
|
function createNativeCheckArgs(
|
|
1415
1455
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
@@ -1440,7 +1480,6 @@ function createNativeCheckArgs(
|
|
|
1440
1480
|
}
|
|
1441
1481
|
args.push(...createNativeCheckThreadingArgs(options, plugin));
|
|
1442
1482
|
args.push(...createNativeCheckDiagnosticsArgs(options, plugin));
|
|
1443
|
-
args.push(...createNativeTsgoArgs(options));
|
|
1444
1483
|
return args;
|
|
1445
1484
|
}
|
|
1446
1485
|
|
|
@@ -1550,15 +1589,37 @@ function transformHostTimingLabel(
|
|
|
1550
1589
|
|
|
1551
1590
|
/**
|
|
1552
1591
|
* Forward the tsgo flags ttsc did not recognize to a native sidecar as one
|
|
1553
|
-
* JSON-encoded
|
|
1554
|
-
*
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
1592
|
+
* JSON-encoded payload. The sidecar replays them through tsgo's own option
|
|
1593
|
+
* parser onto `CompilerOptions`, so a flag like `ttsc --strict` reaches a
|
|
1594
|
+
* plugin build the same way it reaches the plain tsgo lane.
|
|
1595
|
+
*
|
|
1596
|
+
* The payload travels in the `TTSC_TSGO_ARGS` environment variable, not on the
|
|
1597
|
+
* sidecar's command line. #113 shipped it as a `--tsgo-args` flag, which is an
|
|
1598
|
+
* addition to a plugin protocol third-party hosts had already frozen: a Go
|
|
1599
|
+
* `flag.FlagSet` created with `flag.ContinueOnError` treats an undeclared flag
|
|
1600
|
+
* as fatal, so every pre-#113 sidecar answered `flag provided but not defined:
|
|
1601
|
+
* -tsgo-args` and exited 2. That took down `ttsc --strict`, `ttsc
|
|
1602
|
+
* --declaration`, `ttsx --strict` and — because {@link isolatedTsgoOutputArgs}
|
|
1603
|
+
* makes this payload non-empty on its own — plain `ttsc <file.ts>`, on every
|
|
1604
|
+
* project carrying a typia/nestia-era transform host (issue #1188).
|
|
1605
|
+
*
|
|
1606
|
+
* The environment is the channel ttsc already uses for host-owned payloads that
|
|
1607
|
+
* must not collide with a third-party flag set (`TTSC_LINKED_PLUGINS_JSON`,
|
|
1608
|
+
* `TTSC_PLUGIN_CONFIG_DIR`). It reaches those hosts without any change on their
|
|
1609
|
+
* side, because `buildSourcePlugin.ts::sourceBuildWorkspaceReplacements` builds
|
|
1610
|
+
* every source plugin against the installed ttsc's own driver, and
|
|
1611
|
+
* `driver.LoadProgram` reads the variable whenever the caller supplied no
|
|
1612
|
+
* explicit argv. It is strictly better than the capability gate `ad3443a` used
|
|
1613
|
+
* for `--singleThreaded` / `--checkers`: that one drops the flag for hosts that
|
|
1614
|
+
* cannot take it, which is acceptable for a threading knob and not for
|
|
1615
|
+
* `--strict`.
|
|
1616
|
+
*
|
|
1617
|
+
* Returns the JSON payload, or `undefined` when this lane forwards nothing.
|
|
1557
1618
|
*/
|
|
1558
1619
|
function createNativeTsgoArgs(
|
|
1559
1620
|
options: TtscCommonOptions,
|
|
1560
1621
|
leading: readonly string[] = [],
|
|
1561
|
-
): string
|
|
1622
|
+
): string | undefined {
|
|
1562
1623
|
const passthrough = [
|
|
1563
1624
|
// Ahead of the user's own flags, so the same precedence holds here as on
|
|
1564
1625
|
// the direct tsgo lane: whatever the user forwarded wins.
|
|
@@ -1567,9 +1628,9 @@ function createNativeTsgoArgs(
|
|
|
1567
1628
|
...isolatedTsgoOutputArgs(options),
|
|
1568
1629
|
];
|
|
1569
1630
|
if (passthrough.length === 0) {
|
|
1570
|
-
return
|
|
1631
|
+
return undefined;
|
|
1571
1632
|
}
|
|
1572
|
-
return
|
|
1633
|
+
return JSON.stringify(passthrough);
|
|
1573
1634
|
}
|
|
1574
1635
|
|
|
1575
1636
|
function isolatedTsgoOutputArgs(options: TtscCommonOptions): string[] {
|
|
@@ -1678,6 +1739,7 @@ function runNativeCheckPlugins(
|
|
|
1678
1739
|
"ttsc.check",
|
|
1679
1740
|
timing,
|
|
1680
1741
|
`ttsc check plugin ${plugin.name} time`,
|
|
1742
|
+
createNativeTsgoArgs(options),
|
|
1681
1743
|
);
|
|
1682
1744
|
out = appendBuildOutput(out, result);
|
|
1683
1745
|
if (result.status !== 0) {
|
|
@@ -1949,11 +2011,12 @@ function runNativePluginCommand(
|
|
|
1949
2011
|
label: string,
|
|
1950
2012
|
timing: BuildTiming,
|
|
1951
2013
|
timingLabel: string,
|
|
2014
|
+
tsgoArgs?: string,
|
|
1952
2015
|
): TtscBuildResult {
|
|
1953
2016
|
const startedAt = process.hrtime.bigint();
|
|
1954
2017
|
const res = spawnNative(plugin.binary, args, {
|
|
1955
2018
|
cwd: execution.projectRoot,
|
|
1956
|
-
env: nativePluginEnv(options.env, execution, plugin),
|
|
2019
|
+
env: nativePluginEnv(options.env, execution, plugin, tsgoArgs),
|
|
1957
2020
|
encoding: "utf8",
|
|
1958
2021
|
});
|
|
1959
2022
|
recordTiming(timing, timingLabel, startedAt);
|
|
@@ -2047,7 +2110,7 @@ function resolveExecutionContext(
|
|
|
2047
2110
|
cacheDir: options.cacheDir ?? options.env?.TTSC_CACHE_DIR,
|
|
2048
2111
|
cwd,
|
|
2049
2112
|
entries: options.plugins,
|
|
2050
|
-
env:
|
|
2113
|
+
env: inheritedSidecarEnv(options.env),
|
|
2051
2114
|
onWatchInputs: options.onWatchInputs,
|
|
2052
2115
|
pluginConfigDir: options.pluginConfigDir,
|
|
2053
2116
|
projectRoot,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
|
|
4
|
+
import { createCanonicalTempDirectory } from "../../internal/createCanonicalTempDirectory";
|
|
5
5
|
import type { TtscSingleFileEmitOptions } from "../../structures/internal/TtscSingleFileEmitOptions";
|
|
6
6
|
import { readProjectConfig } from "./project/readProjectConfig";
|
|
7
7
|
import { resolveEmittedJavaScript } from "./resolveEmittedJavaScript";
|
|
@@ -32,7 +32,7 @@ export function runSingleFileEmit(options: TtscSingleFileEmitOptions): string {
|
|
|
32
32
|
});
|
|
33
33
|
const tsconfig = project.path;
|
|
34
34
|
const projectRoot = project.root;
|
|
35
|
-
const outDir =
|
|
35
|
+
const outDir = createCanonicalTempDirectory("ttsc-single-file-");
|
|
36
36
|
try {
|
|
37
37
|
const result = runBuild({
|
|
38
38
|
...options,
|