ttsc 0.12.2 → 0.12.4
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/platform/main.go +9 -0
- package/cmd/ttsc/api_compile.go +109 -101
- package/cmd/ttsc/api_compile_branches_test.go +55 -57
- package/cmd/ttsc/api_transform.go +54 -49
- package/cmd/ttsc/api_transform_branches_test.go +30 -32
- package/cmd/ttsc/build.go +111 -108
- package/cmd/ttsc/build_branches_test.go +95 -97
- package/cmd/ttsc/helpers_test.go +39 -28
- package/cmd/ttsc/main.go +111 -104
- package/cmd/ttsc/main_alias_branches_test.go +21 -23
- package/cmd/ttscserver/main.go +96 -90
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +22 -22
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +22 -22
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +23 -23
- package/cmd/utility-host/main.go +30 -23
- package/driver/lsp.go +53 -7
- package/driver/plugins.go +88 -72
- package/driver/program.go +362 -339
- package/driver/rewrite.go +442 -380
- package/internal/lspserver/lsp_envelope.go +64 -64
- package/internal/lspserver/lsp_frame.go +60 -60
- package/internal/lspserver/lsp_plugin.go +57 -57
- package/internal/lspserver/lsp_proxy.go +265 -251
- package/internal/lspserver/lsp_server.go +155 -144
- package/lib/TtscCompiler.d.ts +7 -0
- package/lib/TtscCompiler.js +7 -0
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.d.ts +11 -1
- package/lib/compiler/internal/buildNativeCompiler.js +23 -3
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.d.ts +9 -0
- package/lib/compiler/internal/compileProjectInMemory.js +36 -21
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/paths.d.ts +27 -0
- package/lib/compiler/internal/paths.js +70 -0
- package/lib/compiler/internal/paths.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.d.ts +9 -1
- package/lib/compiler/internal/project/readProjectConfig.js +80 -1
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.d.ts +13 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +24 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.d.ts +9 -1
- package/lib/compiler/internal/resolveBinary.js +17 -1
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +16 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +42 -10
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +21 -1
- package/lib/compiler/internal/resolveTsgo.js +23 -1
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +19 -0
- package/lib/compiler/internal/runBuild.js +112 -46
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.d.ts +10 -1
- package/lib/compiler/internal/runSingleFileEmit.js +14 -1
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +5 -0
- package/lib/compiler/internal/sharedHostHelpers.js +6 -2
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +21 -0
- package/lib/compiler/internal/spawnNative.js +59 -0
- package/lib/compiler/internal/spawnNative.js.map +1 -0
- package/lib/compiler/internal/transformProjectInMemory.d.ts +15 -1
- package/lib/compiler/internal/transformProjectInMemory.js +57 -61
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/getCompilerVersionText.js +7 -0
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +32 -0
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveCacheDir.d.ts +9 -0
- package/lib/launcher/internal/resolveCacheDir.js +22 -0
- package/lib/launcher/internal/resolveCacheDir.js.map +1 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js +2 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -1
- package/lib/launcher/internal/runTtsc.d.ts +10 -0
- package/lib/launcher/internal/runTtsc.js +101 -16
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +10 -35
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.d.ts +8 -0
- package/lib/launcher/internal/runTtsx.js +33 -7
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +38 -0
- package/lib/plugin/internal/buildSourcePlugin.js +40 -17
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +26 -0
- package/lib/plugin/internal/loadProjectPlugins.js +32 -18
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +15 -2
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +0 -2
- package/package.json +9 -9
- package/shim/ast/go.mod +9 -0
- package/shim/ast/go.sum +20 -0
- package/shim/ast/safe_text.go +86 -0
- package/shim/ast/shim.go +24 -0
- package/shim/ast/test/node_text_joins_multi_hop_qualified_name_test.go +32 -0
- package/shim/ast/test/node_text_joins_one_hop_qualified_name_test.go +31 -0
- package/shim/ast/test/node_text_returns_empty_for_nil_test.go +23 -0
- package/shim/ast/test/node_text_returns_identifier_text_test.go +27 -0
- package/shim/ast/test/node_text_skips_empty_qualified_left_test.go +30 -0
- package/shim/checker/shim.go +35 -0
- package/shim/core/shim.go +17 -0
- package/shim/diagnosticwriter/shim.go +4 -0
- package/shim/lsp/shim.go +3 -3
- package/shim/printer/shim.go +20 -0
- package/src/TtscCompiler.ts +7 -0
- package/src/compiler/internal/buildNativeCompiler.ts +23 -3
- package/src/compiler/internal/compileProjectInMemory.ts +34 -25
- package/src/compiler/internal/paths.ts +68 -0
- package/src/compiler/internal/project/readProjectConfig.ts +86 -1
- package/src/compiler/internal/project/resolveProjectConfig.ts +24 -1
- package/src/compiler/internal/resolveBinary.ts +17 -1
- package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
- package/src/compiler/internal/resolveTsgo.ts +29 -1
- package/src/compiler/internal/runBuild.ts +110 -55
- package/src/compiler/internal/runSingleFileEmit.ts +14 -1
- package/src/compiler/internal/sharedHostHelpers.ts +7 -7
- package/src/compiler/internal/spawnNative.ts +64 -0
- package/src/compiler/internal/transformProjectInMemory.ts +47 -67
- package/src/launcher/internal/getCompilerVersionText.ts +7 -0
- package/src/launcher/internal/prepareExecution.ts +32 -0
- package/src/launcher/internal/resolveCacheDir.ts +19 -0
- package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
- package/src/launcher/internal/runTtsc.ts +120 -20
- package/src/launcher/internal/runTtscserver.ts +9 -1
- package/src/launcher/internal/runTtsx.ts +33 -8
- package/src/plugin/internal/buildSourcePlugin.ts +40 -14
- package/src/plugin/internal/loadProjectPlugins.ts +42 -18
- package/src/structures/ITtscCompilerTransformation.ts +15 -2
- package/src/structures/ITtscPlugin.ts +0 -1
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
- package/tools/gen_shims/main.go +43 -8
- package/utility/host.go +294 -245
|
@@ -9,12 +9,22 @@ import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiag
|
|
|
9
9
|
import type { ITtscParsedProjectConfig } from "../../structures/internal/ITtscParsedProjectConfig";
|
|
10
10
|
import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
|
|
11
11
|
import { buildNativeCompiler } from "./buildNativeCompiler";
|
|
12
|
+
import { isOutsideRelativePath, packageRootDir } from "./paths";
|
|
12
13
|
import { readProjectConfig } from "./project/readProjectConfig";
|
|
13
14
|
import { runBuild } from "./runBuild";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Compile a project and capture emitted files without writing to the project
|
|
17
18
|
* tree.
|
|
19
|
+
*
|
|
20
|
+
* When no plugins are configured the fast path spawns the native ttsc compiler
|
|
21
|
+
* host (`cmd/ttsc api-compile`) which returns a structured JSON response
|
|
22
|
+
* containing diagnostics and an output file map. When plugins are present the
|
|
23
|
+
* slow path goes through `runBuild` into a temp directory and reads the files
|
|
24
|
+
* back from disk.
|
|
25
|
+
*
|
|
26
|
+
* @returns A map of output path → file content plus a `TtscBuildResult` with
|
|
27
|
+
* diagnostics and the exit status.
|
|
18
28
|
*/
|
|
19
29
|
export function compileProjectInMemory(options: ITtscCompilerContext): {
|
|
20
30
|
output: Record<string, string>;
|
|
@@ -64,6 +74,7 @@ export function compileProjectInMemory(options: ITtscCompilerContext): {
|
|
|
64
74
|
};
|
|
65
75
|
}
|
|
66
76
|
|
|
77
|
+
/** Return true when the project or the call-level options declare any plugins. */
|
|
67
78
|
function hasConfiguredPlugins(
|
|
68
79
|
options: ITtscCompilerContext,
|
|
69
80
|
project: ITtscParsedProjectConfig,
|
|
@@ -71,6 +82,10 @@ function hasConfiguredPlugins(
|
|
|
71
82
|
return hasProjectPluginEntries(project, options.plugins);
|
|
72
83
|
}
|
|
73
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Plugin-backed compilation: emit into a temp directory via `runBuild`, then
|
|
87
|
+
* read back every file the build wrote so they can be returned as strings.
|
|
88
|
+
*/
|
|
74
89
|
function compileProjectWithPlugins(
|
|
75
90
|
options: ITtscCompilerContext,
|
|
76
91
|
cwd: string,
|
|
@@ -101,6 +116,15 @@ function compileProjectWithPlugins(
|
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Build a function that maps a path relative to the temp output directory to
|
|
121
|
+
* the key used in the returned `output` map.
|
|
122
|
+
*
|
|
123
|
+
* When `outDir` is inside the project root the key is relative to the project
|
|
124
|
+
* root (preserving the `outDir` prefix). When `outDir` is outside the project
|
|
125
|
+
* root the key is absolute-style (`/absolute/outDir/relative`). When `outDir`
|
|
126
|
+
* is absent the key is the bare relative path.
|
|
127
|
+
*/
|
|
104
128
|
function outputKeyMapper(
|
|
105
129
|
project: ITtscParsedProjectConfig,
|
|
106
130
|
): (relativePath: string) => string {
|
|
@@ -116,14 +140,7 @@ function outputKeyMapper(
|
|
|
116
140
|
return (relativePath) => pathToKey(path.join(outDir, relativePath));
|
|
117
141
|
}
|
|
118
142
|
|
|
119
|
-
|
|
120
|
-
return (
|
|
121
|
-
relative === ".." ||
|
|
122
|
-
relative.startsWith(`..${path.sep}`) ||
|
|
123
|
-
path.isAbsolute(relative)
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
143
|
+
/** Read every file in `directory` recursively and return a `path→content` map. */
|
|
127
144
|
function readOutputDirectory(
|
|
128
145
|
directory: string,
|
|
129
146
|
keyOf: (relativePath: string) => string,
|
|
@@ -141,6 +158,7 @@ function readOutputDirectory(
|
|
|
141
158
|
return output;
|
|
142
159
|
}
|
|
143
160
|
|
|
161
|
+
/** Recursively list all files under `directory`, sorted for stable output. */
|
|
144
162
|
function listFiles(directory: string): string[] {
|
|
145
163
|
const out: string[] = [];
|
|
146
164
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
@@ -154,10 +172,18 @@ function listFiles(directory: string): string[] {
|
|
|
154
172
|
return out.sort();
|
|
155
173
|
}
|
|
156
174
|
|
|
175
|
+
/** Normalise a file path to a forward-slash key suitable for the output map. */
|
|
157
176
|
function pathToKey(file: string): string {
|
|
158
177
|
return file.replace(/\\/g, "/");
|
|
159
178
|
}
|
|
160
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Parse the JSON envelope written by the native compiler host to stdout.
|
|
182
|
+
*
|
|
183
|
+
* On success returns `{ diagnostics, output }`. On JSON parse failure throws a
|
|
184
|
+
* descriptive error using stderr (preferred) or stdout as context, so callers
|
|
185
|
+
* see the original compiler error rather than a generic JSON parse message.
|
|
186
|
+
*/
|
|
161
187
|
function parseNativeCompileOutput(
|
|
162
188
|
stdout: string,
|
|
163
189
|
stderr: string,
|
|
@@ -181,20 +207,3 @@ function parseNativeCompileOutput(
|
|
|
181
207
|
);
|
|
182
208
|
}
|
|
183
209
|
}
|
|
184
|
-
|
|
185
|
-
function packageRootDir(): string {
|
|
186
|
-
let current = path.resolve(__dirname);
|
|
187
|
-
while (true) {
|
|
188
|
-
if (
|
|
189
|
-
fs.existsSync(path.join(current, "package.json")) &&
|
|
190
|
-
fs.existsSync(path.join(current, "go.mod"))
|
|
191
|
-
) {
|
|
192
|
-
return fs.realpathSync.native?.(current) ?? fs.realpathSync(current);
|
|
193
|
-
}
|
|
194
|
-
const parent = path.dirname(current);
|
|
195
|
-
if (parent === current) {
|
|
196
|
-
throw new Error("ttsc: package root not found for native compiler build");
|
|
197
|
-
}
|
|
198
|
-
current = parent;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Walk up the directory tree from `__dirname` until a directory that contains
|
|
6
|
+
* both `package.json` and `go.mod` is found. This is the `packages/ttsc` root
|
|
7
|
+
* and must be passed to `buildNativeCompiler` so it can locate `cmd/ttsc`.
|
|
8
|
+
*
|
|
9
|
+
* Uses `fs.realpathSync.native` when available to resolve symlinks identically
|
|
10
|
+
* to the Go toolchain's own path resolution.
|
|
11
|
+
*
|
|
12
|
+
* Throws when the root cannot be found (i.e. the package is not in a Go
|
|
13
|
+
* workspace), which would indicate a broken installation.
|
|
14
|
+
*/
|
|
15
|
+
export function packageRootDir(): string {
|
|
16
|
+
let current = path.resolve(__dirname);
|
|
17
|
+
while (true) {
|
|
18
|
+
if (
|
|
19
|
+
fs.existsSync(path.join(current, "package.json")) &&
|
|
20
|
+
fs.existsSync(path.join(current, "go.mod"))
|
|
21
|
+
) {
|
|
22
|
+
return fs.realpathSync.native?.(current) ?? fs.realpathSync(current);
|
|
23
|
+
}
|
|
24
|
+
const parent = path.dirname(current);
|
|
25
|
+
if (parent === current) {
|
|
26
|
+
throw new Error("ttsc: package root not found for native compiler build");
|
|
27
|
+
}
|
|
28
|
+
current = parent;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Walk up the directory tree from `from` looking for a `go.mod` file. Stops
|
|
34
|
+
* after `maxDepth` hops so callers can bound the search to a known
|
|
35
|
+
* neighbourhood of the plugin source directory.
|
|
36
|
+
*
|
|
37
|
+
* Returns the absolute path to the first `go.mod` found, or `null` when no
|
|
38
|
+
* `go.mod` exists within the depth limit or filesystem root is reached first.
|
|
39
|
+
*/
|
|
40
|
+
export function findNearestGoMod(
|
|
41
|
+
from: string,
|
|
42
|
+
maxDepth: number,
|
|
43
|
+
): string | null {
|
|
44
|
+
let current = path.resolve(from);
|
|
45
|
+
let depth = 0;
|
|
46
|
+
while (true) {
|
|
47
|
+
const candidate = path.join(current, "go.mod");
|
|
48
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
49
|
+
if (depth >= maxDepth) return null;
|
|
50
|
+
const parent = path.dirname(current);
|
|
51
|
+
if (parent === current) return null;
|
|
52
|
+
current = parent;
|
|
53
|
+
depth += 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Return `true` when a relative path escapes its base directory — i.e. starts
|
|
59
|
+
* with `..` or is an absolute path. Used by callers that need to guard against
|
|
60
|
+
* path traversal before building output keys or spawning subprocesses.
|
|
61
|
+
*/
|
|
62
|
+
export function isOutsideRelativePath(relative: string): boolean {
|
|
63
|
+
return (
|
|
64
|
+
relative === ".." ||
|
|
65
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
66
|
+
path.isAbsolute(relative)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -7,18 +7,36 @@ import type { ITtscParsedProjectConfig } from "../../../structures/internal/ITts
|
|
|
7
7
|
import type { ITtscProjectLocatorOptions } from "../../../structures/internal/ITtscProjectLocatorOptions";
|
|
8
8
|
import { resolveProjectConfig } from "./resolveProjectConfig";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Compiler option keys whose values are file-system paths that must be resolved
|
|
12
|
+
* relative to the tsconfig that declares them, not the project root.
|
|
13
|
+
*/
|
|
10
14
|
const PATH_OPTIONS = new Set(["baseUrl", "declarationDir", "rootDir"]);
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Intermediate type used during `extends`-chain resolution before the result is
|
|
18
|
+
* projected into `ITtscParsedProjectConfig`.
|
|
19
|
+
*/
|
|
12
20
|
type ResolvedCompilerOptions = {
|
|
13
21
|
options: Record<string, unknown>;
|
|
22
|
+
/** Directory of the tsconfig that last declared each option key. */
|
|
14
23
|
optionBaseDirs: Record<string, string>;
|
|
15
24
|
outDir?: string;
|
|
16
25
|
pluginBaseDirs: string[];
|
|
26
|
+
/** True when any tsconfig in the chain explicitly declared `plugins`. */
|
|
17
27
|
pluginsDeclared: boolean;
|
|
18
28
|
plugins: ITtscProjectPluginConfig[];
|
|
19
29
|
};
|
|
20
30
|
|
|
21
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Read and resolve the project config subset used by ttsc.
|
|
33
|
+
*
|
|
34
|
+
* Follows `extends` chains (including arrays) and merges compiler options with
|
|
35
|
+
* later configs taking precedence. Path-typed options (`baseUrl`,
|
|
36
|
+
* `declarationDir`, `rootDir`) are resolved relative to the config that
|
|
37
|
+
* declares them. The `outDir` is resolved to an absolute path. Plugins are
|
|
38
|
+
* inherited from the nearest ancestor that declares them.
|
|
39
|
+
*/
|
|
22
40
|
export function readProjectConfig(
|
|
23
41
|
opts: ITtscProjectLocatorOptions = {},
|
|
24
42
|
): ITtscParsedProjectConfig {
|
|
@@ -39,12 +57,20 @@ export function readProjectConfig(
|
|
|
39
57
|
};
|
|
40
58
|
}
|
|
41
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Type guard: accept any non-null object as a plugin config entry. This is
|
|
62
|
+
* intentionally loose because the shape is validated later by plugin loaders.
|
|
63
|
+
*/
|
|
42
64
|
function isProjectPluginConfig(
|
|
43
65
|
value: unknown,
|
|
44
66
|
): value is ITtscProjectPluginConfig {
|
|
45
67
|
return typeof value === "object" && value !== null;
|
|
46
68
|
}
|
|
47
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Resolve symlinks on `location`, returning the original path when
|
|
72
|
+
* `realpathSync` fails (e.g. when the file does not yet exist).
|
|
73
|
+
*/
|
|
48
74
|
function resolveRealPath(location: string): string {
|
|
49
75
|
try {
|
|
50
76
|
return fs.realpathSync(location);
|
|
@@ -53,10 +79,18 @@ function resolveRealPath(location: string): string {
|
|
|
53
79
|
}
|
|
54
80
|
}
|
|
55
81
|
|
|
82
|
+
/** Resolve `target` against `cwd` when it is not already absolute. */
|
|
56
83
|
function resolveAbsolutePath(cwd: string, target: string): string {
|
|
57
84
|
return path.isAbsolute(target) ? target : path.resolve(cwd, target);
|
|
58
85
|
}
|
|
59
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Recursively read and merge `compilerOptions` from `tsconfig` and all configs
|
|
89
|
+
* in its `extends` chain. `seen` tracks canonical paths to detect circular
|
|
90
|
+
* `extends` references; the current config is removed from `seen` in the
|
|
91
|
+
* `finally` block so sibling-referenced configs can be visited again via a
|
|
92
|
+
* different parent.
|
|
93
|
+
*/
|
|
60
94
|
function readResolvedCompilerOptions(
|
|
61
95
|
tsconfig: string,
|
|
62
96
|
seen: Set<string> = new Set(),
|
|
@@ -117,6 +151,16 @@ function readResolvedCompilerOptions(
|
|
|
117
151
|
}
|
|
118
152
|
}
|
|
119
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Resolve the base compiler options from the `extends` field of a tsconfig.
|
|
156
|
+
*
|
|
157
|
+
* - String: a single base config; options are returned directly.
|
|
158
|
+
* - Array: multiple base configs merged left-to-right (later entries win).
|
|
159
|
+
* - Absent / non-string non-array: returns empty options.
|
|
160
|
+
*
|
|
161
|
+
* Plugin inheritance: when the current config in an array chain explicitly
|
|
162
|
+
* declares `plugins`, subsequent configs in the array do not override them.
|
|
163
|
+
*/
|
|
120
164
|
function resolveBaseCompilerOptions(
|
|
121
165
|
tsconfig: string,
|
|
122
166
|
extended: unknown,
|
|
@@ -172,6 +216,10 @@ function resolveBaseCompilerOptions(
|
|
|
172
216
|
return merged;
|
|
173
217
|
}
|
|
174
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Resolve path-typed compiler options to absolute paths using the directory of
|
|
221
|
+
* the tsconfig that declared each option (`baseDirs`).
|
|
222
|
+
*/
|
|
175
223
|
function resolvePathOptions(
|
|
176
224
|
options: Record<string, unknown>,
|
|
177
225
|
baseDirs: Record<string, string>,
|
|
@@ -187,6 +235,16 @@ function resolvePathOptions(
|
|
|
187
235
|
return resolved;
|
|
188
236
|
}
|
|
189
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Resolve an `extends` specifier to an absolute path using the same rules as
|
|
240
|
+
* TypeScript's config resolution:
|
|
241
|
+
*
|
|
242
|
+
* - Absolute path: used directly.
|
|
243
|
+
* - Relative specifier (`./`, `../`, `.`, `..`): resolved against the declaring
|
|
244
|
+
* tsconfig's directory with fallback extension candidates.
|
|
245
|
+
* - Bare package specifier: resolved through Node's module resolver (the resolver
|
|
246
|
+
* also tries appending `.json` when the first attempt fails).
|
|
247
|
+
*/
|
|
190
248
|
function resolveExtendsConfig(tsconfig: string, specifier: string): string {
|
|
191
249
|
const baseDir = path.dirname(tsconfig);
|
|
192
250
|
if (path.isAbsolute(specifier)) {
|
|
@@ -203,6 +261,10 @@ function resolveExtendsConfig(tsconfig: string, specifier: string): string {
|
|
|
203
261
|
}
|
|
204
262
|
}
|
|
205
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Given an on-disk location, try the path as-is, with `.json` appended, and as
|
|
266
|
+
* a directory containing `tsconfig.json`. Returns the first match or throws.
|
|
267
|
+
*/
|
|
206
268
|
function resolveExistingExtendsPath(location: string): string {
|
|
207
269
|
const candidates = new Set<string>([
|
|
208
270
|
location,
|
|
@@ -217,6 +279,10 @@ function resolveExistingExtendsPath(location: string): string {
|
|
|
217
279
|
throw new Error(`ttsc: extended tsconfig not found: ${location}`);
|
|
218
280
|
}
|
|
219
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Return true when `specifier` is a relative path reference: `.`, `..`, or a
|
|
284
|
+
* string starting with `./`, `../`, `.\\`, or `..\\`.
|
|
285
|
+
*/
|
|
220
286
|
function isRelativeSpecifier(specifier: string): boolean {
|
|
221
287
|
return (
|
|
222
288
|
specifier === "." ||
|
|
@@ -228,10 +294,19 @@ function isRelativeSpecifier(specifier: string): boolean {
|
|
|
228
294
|
);
|
|
229
295
|
}
|
|
230
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Parse a JSONC (JSON with Comments) string by stripping comments and trailing
|
|
299
|
+
* commas before handing off to `JSON.parse`.
|
|
300
|
+
*/
|
|
231
301
|
function parseJsonc(input: string): unknown {
|
|
232
302
|
return JSON.parse(stripTrailingCommas(stripComments(input)));
|
|
233
303
|
}
|
|
234
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Remove `//` line comments and `/* block comments *\/` from a JSONC string.
|
|
307
|
+
* Correctly handles strings that contain comment-like character sequences by
|
|
308
|
+
* tracking string boundaries and escape characters.
|
|
309
|
+
*/
|
|
235
310
|
function stripComments(input: string): string {
|
|
236
311
|
let output = "";
|
|
237
312
|
let inBlockComment = false;
|
|
@@ -292,6 +367,11 @@ function stripComments(input: string): string {
|
|
|
292
367
|
return output;
|
|
293
368
|
}
|
|
294
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Remove trailing commas before `}` or `]` from a JSON string (after comments
|
|
372
|
+
* have already been stripped). Handles string boundaries and escape characters
|
|
373
|
+
* to avoid removing commas inside string values.
|
|
374
|
+
*/
|
|
295
375
|
function stripTrailingCommas(input: string): string {
|
|
296
376
|
let output = "";
|
|
297
377
|
let inString = false;
|
|
@@ -330,6 +410,11 @@ function stripTrailingCommas(input: string): string {
|
|
|
330
410
|
return output;
|
|
331
411
|
}
|
|
332
412
|
|
|
413
|
+
/**
|
|
414
|
+
* Return the first non-whitespace character at or after position `from` in
|
|
415
|
+
* `input`, or `undefined` when only whitespace remains. Used by
|
|
416
|
+
* `stripTrailingCommas` to detect whether a comma is trailing.
|
|
417
|
+
*/
|
|
333
418
|
function nextNonWhitespace(input: string, from: number): string | undefined {
|
|
334
419
|
for (let i = from; i < input.length; i += 1) {
|
|
335
420
|
const current = input[i]!;
|
|
@@ -3,7 +3,19 @@ import path from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import type { ITtscProjectLocatorOptions } from "../../../structures/internal/ITtscProjectLocatorOptions";
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the tsconfig/jsconfig that owns a ttsc invocation.
|
|
8
|
+
*
|
|
9
|
+
* Resolution order:
|
|
10
|
+
*
|
|
11
|
+
* 1. `opts.tsconfig` — resolved absolute and checked for existence.
|
|
12
|
+
* 2. `opts.file` — the nearest ancestor config that contains the file is found by
|
|
13
|
+
* walking up from the file's directory.
|
|
14
|
+
* 3. `opts.cwd` — the nearest ancestor config walking up from cwd.
|
|
15
|
+
*
|
|
16
|
+
* Always returns a real (symlink-resolved) absolute path. Throws when no config
|
|
17
|
+
* is found or the explicitly supplied path does not exist.
|
|
18
|
+
*/
|
|
7
19
|
export function resolveProjectConfig(
|
|
8
20
|
opts: ITtscProjectLocatorOptions = {},
|
|
9
21
|
): string {
|
|
@@ -29,10 +41,15 @@ export function resolveProjectConfig(
|
|
|
29
41
|
return resolveRealPath(found);
|
|
30
42
|
}
|
|
31
43
|
|
|
44
|
+
/** Resolve `target` against `cwd` when it is not already absolute. */
|
|
32
45
|
function resolveAbsolutePath(cwd: string, target: string): string {
|
|
33
46
|
return path.isAbsolute(target) ? target : path.resolve(cwd, target);
|
|
34
47
|
}
|
|
35
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Resolve symlinks on `location`, returning the original path when
|
|
51
|
+
* `realpathSync` fails (e.g. when the file does not yet exist).
|
|
52
|
+
*/
|
|
36
53
|
function resolveRealPath(location: string): string {
|
|
37
54
|
try {
|
|
38
55
|
return fs.realpathSync(location);
|
|
@@ -41,6 +58,11 @@ function resolveRealPath(location: string): string {
|
|
|
41
58
|
}
|
|
42
59
|
}
|
|
43
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Walk up the directory tree from `from`, returning the first directory that
|
|
63
|
+
* contains a file whose name is in `names`. Returns `null` when the filesystem
|
|
64
|
+
* root is reached without finding a match.
|
|
65
|
+
*/
|
|
44
66
|
function findUp(from: string, names: readonly string[]): string | null {
|
|
45
67
|
let current = path.resolve(from);
|
|
46
68
|
while (true) {
|
|
@@ -58,6 +80,7 @@ function findUp(from: string, names: readonly string[]): string | null {
|
|
|
58
80
|
}
|
|
59
81
|
}
|
|
60
82
|
|
|
83
|
+
/** Return true when `location` exists and is a directory. */
|
|
61
84
|
function isDirectory(location: string): boolean {
|
|
62
85
|
try {
|
|
63
86
|
return fs.statSync(location).isDirectory();
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the ttsc helper binary path, or null when unavailable.
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
*
|
|
9
|
+
* 1. `TTSC_BINARY` env var — must be an absolute path when set.
|
|
10
|
+
* 2. The per-platform npm package `@ttsc/<platform>-<arch>/bin/ttsc[.exe]`.
|
|
11
|
+
* 3. A `ttsc-native[.exe]` sibling of this package's root (dev/CI layout).
|
|
12
|
+
*/
|
|
5
13
|
export function resolveBinary(
|
|
6
14
|
opts: { env?: NodeJS.ProcessEnv } = {},
|
|
7
15
|
): string | null {
|
|
@@ -24,6 +32,10 @@ export function resolveBinary(
|
|
|
24
32
|
return null;
|
|
25
33
|
}
|
|
26
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Return the path to the dev-layout `ttsc-native[.exe]` binary adjacent to the
|
|
37
|
+
* package root, or null when the file does not exist.
|
|
38
|
+
*/
|
|
27
39
|
function defaultLocalBinaryPath(): string | null {
|
|
28
40
|
const root = packageRootDir();
|
|
29
41
|
const candidate = path.resolve(
|
|
@@ -35,6 +47,10 @@ function defaultLocalBinaryPath(): string | null {
|
|
|
35
47
|
return fs.existsSync(candidate) ? candidate : null;
|
|
36
48
|
}
|
|
37
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Return the real, absolute path of the `packages/ttsc` package root by walking
|
|
52
|
+
* two levels up from `__dirname` (which lives in `src/compiler/internal`).
|
|
53
|
+
*/
|
|
38
54
|
function packageRootDir(): string {
|
|
39
55
|
const moduleDir = path.resolve(__dirname, "..", "..");
|
|
40
56
|
return fs.realpathSync.native?.(moduleDir) ?? fs.realpathSync(moduleDir);
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { isOutsideRelativePath } from "./paths";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Locate the JavaScript file emitted for a TypeScript source file.
|
|
8
|
+
*
|
|
9
|
+
* Resolution strategy:
|
|
10
|
+
*
|
|
11
|
+
* 1. Try to derive the exact output path by mirroring the source's relative
|
|
12
|
+
* position inside `projectRoot` into `outDir`, applying the correct JS
|
|
13
|
+
* extension (`.js` / `.mjs` / `.cjs`). Use this path if it exists on disk.
|
|
14
|
+
* 2. Fall back to scoring each candidate in `emittedFiles` (or a recursive
|
|
15
|
+
* directory scan of `outDir`) by the number of trailing path-stem segments
|
|
16
|
+
* shared with the source file name, and pick the highest-scoring existing
|
|
17
|
+
* file.
|
|
18
|
+
*
|
|
19
|
+
* Returns `null` when no matching output file is found on disk.
|
|
20
|
+
*/
|
|
5
21
|
export function resolveEmittedJavaScript(options: {
|
|
22
|
+
/** Pre-computed list of emitted paths; when absent `outDir` is scanned. */
|
|
6
23
|
emittedFiles?: readonly string[];
|
|
7
24
|
outDir: string;
|
|
8
25
|
projectRoot: string;
|
|
@@ -31,6 +48,11 @@ export function resolveEmittedJavaScript(options: {
|
|
|
31
48
|
return best && fs.existsSync(best) ? best : null;
|
|
32
49
|
}
|
|
33
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Derive the exact output path for `sourceFile` by mirroring its position
|
|
53
|
+
* relative to `projectRoot` into `outDir`. Returns `null` when the source is
|
|
54
|
+
* not inside the project root or when the path cannot be determined.
|
|
55
|
+
*/
|
|
34
56
|
function resolveExactEmittedFile(
|
|
35
57
|
outDir: string,
|
|
36
58
|
projectRoot: string,
|
|
@@ -47,6 +69,11 @@ function resolveExactEmittedFile(
|
|
|
47
69
|
);
|
|
48
70
|
}
|
|
49
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Recursively enumerate every JavaScript output file under `root`. Uses an
|
|
74
|
+
* explicit stack instead of recursion to avoid call-stack overflow on deep
|
|
75
|
+
* directory trees. Non-existent roots are silently skipped.
|
|
76
|
+
*/
|
|
50
77
|
function listJavaScriptFiles(root: string): string[] {
|
|
51
78
|
const out: string[] = [];
|
|
52
79
|
const stack = [root];
|
|
@@ -65,15 +92,22 @@ function listJavaScriptFiles(root: string): string[] {
|
|
|
65
92
|
return out;
|
|
66
93
|
}
|
|
67
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Count the number of consecutive trailing path-stem segments that `outPath`
|
|
97
|
+
* and `srcPath` share when both are stripped of their extensions and normalised
|
|
98
|
+
* to forward slashes.
|
|
99
|
+
*
|
|
100
|
+
* Example: `dist/lib/foo.js` vs `src/lib/foo.ts` → 2 (`lib`, `foo`).
|
|
101
|
+
*/
|
|
68
102
|
function sharedSourceStemSegments(outPath: string, srcPath: string): number {
|
|
69
|
-
const
|
|
103
|
+
const stripExtAndSplit = (location: string): string[] => {
|
|
70
104
|
const normalized = location.replace(/\\/g, "/");
|
|
71
105
|
return normalized
|
|
72
106
|
.slice(0, normalized.length - path.extname(normalized).length)
|
|
73
107
|
.split("/");
|
|
74
108
|
};
|
|
75
|
-
const a =
|
|
76
|
-
const b =
|
|
109
|
+
const a = stripExtAndSplit(outPath);
|
|
110
|
+
const b = stripExtAndSplit(srcPath);
|
|
77
111
|
const count = Math.min(a.length, b.length);
|
|
78
112
|
let shared = 0;
|
|
79
113
|
for (let i = 1; i <= count; i += 1) {
|
|
@@ -83,14 +117,10 @@ function sharedSourceStemSegments(outPath: string, srcPath: string): number {
|
|
|
83
117
|
return shared;
|
|
84
118
|
}
|
|
85
119
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
path.isAbsolute(relative)
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Map a TypeScript source extension to its JavaScript output counterpart.
|
|
122
|
+
* `.mts` → `.mjs`, `.cts` → `.cjs`, everything else → `.js`.
|
|
123
|
+
*/
|
|
94
124
|
function emittedJavaScriptExtension(filename: string): string {
|
|
95
125
|
switch (path.extname(filename).toLowerCase()) {
|
|
96
126
|
case ".mts":
|
|
@@ -102,6 +132,7 @@ function emittedJavaScriptExtension(filename: string): string {
|
|
|
102
132
|
}
|
|
103
133
|
}
|
|
104
134
|
|
|
135
|
+
/** Return true when `filename` has a `.js`, `.mjs`, or `.cjs` extension. */
|
|
105
136
|
function isJavaScriptOutput(filename: string): boolean {
|
|
106
137
|
return /\.(?:[cm]?js)$/i.test(filename);
|
|
107
138
|
}
|
|
@@ -2,12 +2,32 @@ import fs from "node:fs";
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the consumer project's TypeScript-Go preview binary and metadata.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order:
|
|
9
|
+
*
|
|
10
|
+
* 1. `opts.binary` or `TTSC_TSGO_BINARY` env var — must be an existing absolute
|
|
11
|
+
* path; returns `{ binary, packageJson: "", packageRoot, version: "custom"
|
|
12
|
+
* }`.
|
|
13
|
+
* 2. `@typescript/native-preview` resolved from the project `cwd`.
|
|
14
|
+
* 3. `@typescript/native-preview` resolved from `opts.resolveFrom` (for test
|
|
15
|
+
* harnesses and embedders that anchor to a different directory).
|
|
16
|
+
*
|
|
17
|
+
* Throws a descriptive error when the package or platform binary is missing so
|
|
18
|
+
* callers never have to reason about undefined binary paths.
|
|
19
|
+
*/
|
|
6
20
|
export function resolveTsgo(
|
|
7
21
|
opts: {
|
|
22
|
+
/** Explicit path to a tsgo binary; bypasses package resolution. */
|
|
8
23
|
binary?: string;
|
|
24
|
+
/** Directory from which to discover `@typescript/native-preview`. */
|
|
9
25
|
cwd?: string;
|
|
10
26
|
env?: NodeJS.ProcessEnv;
|
|
27
|
+
/**
|
|
28
|
+
* Fallback resolution anchor used when the package is not found at `cwd`.
|
|
29
|
+
* Useful in test harnesses that install the package into a different tree.
|
|
30
|
+
*/
|
|
11
31
|
resolveFrom?: string;
|
|
12
32
|
} = {},
|
|
13
33
|
) {
|
|
@@ -28,6 +48,8 @@ export function resolveTsgo(
|
|
|
28
48
|
}
|
|
29
49
|
|
|
30
50
|
const cwd = path.resolve(opts.cwd ?? process.cwd());
|
|
51
|
+
// Resolve the package.json of @typescript/native-preview.
|
|
52
|
+
// Try cwd first, then the optional resolveFrom anchor.
|
|
31
53
|
let packageJson: string;
|
|
32
54
|
packageJson =
|
|
33
55
|
resolveNativePreviewPackageJson(path.join(cwd, "package.json")) ??
|
|
@@ -84,6 +106,11 @@ export function resolveTsgo(
|
|
|
84
106
|
};
|
|
85
107
|
}
|
|
86
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Attempt to resolve the `package.json` of `@typescript/native-preview`
|
|
111
|
+
* starting from `from` (a package.json path or directory). Returns `undefined`
|
|
112
|
+
* when the package is not resolvable from that location.
|
|
113
|
+
*/
|
|
87
114
|
function resolveNativePreviewPackageJson(from: string): string | undefined {
|
|
88
115
|
try {
|
|
89
116
|
return createRequire(from).resolve(
|
|
@@ -94,6 +121,7 @@ function resolveNativePreviewPackageJson(from: string): string | undefined {
|
|
|
94
121
|
}
|
|
95
122
|
}
|
|
96
123
|
|
|
124
|
+
/** Parse a JSON file and return the result as a plain object. */
|
|
97
125
|
function readPackageJson(file: string): Record<string, unknown> {
|
|
98
126
|
return JSON.parse(fs.readFileSync(file, "utf8")) as Record<string, unknown>;
|
|
99
127
|
}
|