ttsc 0.19.3 → 0.20.1
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 +36 -34
- package/cmd/graphdump/main.go +2 -4
- package/cmd/ttsc/filter.go +12 -4
- package/cmd/ttsc/filter_resolves_flag_names_case_insensitively_test.go +53 -0
- package/cmd/ttsc/flags_gen.go +3 -3
- package/cmd/ttscgraph/dump.go +5 -7
- package/cmd/ttscgraph/run_dump_emits_only_checker_verified_member_relations_test.go +118 -0
- package/cmd/ttscgraph/serve.go +58 -308
- package/cmd/ttscgraph/serve_session_keeps_base_url_fallback_unchanged_test.go +49 -0
- package/cmd/ttscgraph/serve_session_keeps_esm_directory_candidate_unchanged_test.go +61 -0
- package/cmd/ttscgraph/serve_session_keeps_exports_blocked_file_candidates_unchanged_test.go +64 -0
- package/cmd/ttscgraph/serve_session_keeps_inactive_conditional_export_unchanged_test.go +66 -0
- package/cmd/ttscgraph/serve_session_keeps_lower_priority_module_candidate_unchanged_test.go +67 -0
- package/cmd/ttscgraph/serve_session_keeps_lower_priority_paths_pattern_unchanged_test.go +58 -0
- package/cmd/ttscgraph/serve_session_keeps_nonlongest_root_dirs_candidate_unchanged_test.go +54 -0
- package/cmd/ttscgraph/serve_session_reloads_commonjs_package_entry_for_esm_test.go +50 -0
- package/cmd/ttscgraph/serve_session_reloads_module_suffix_candidate_test.go +41 -0
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +216 -0
- package/cmd/ttscgraph/serve_session_retries_failed_dump_test.go +103 -0
- package/driver/graph.go +12 -6
- package/driver/lsp.go +30 -0
- package/driver/program.go +37 -1
- package/driver/resolution_candidates.go +819 -0
- package/driver/windowsjunction/windowsjunction.go +40 -0
- package/go.mod +2 -0
- package/internal/graph/build.go +45 -11
- package/internal/graph/dump.go +276 -100
- package/internal/graph/dump_checkout_paths_are_stable_test.go +210 -0
- package/internal/graph/dump_diagnostics_ride_the_generation_that_built_it_test.go +9 -3
- package/internal/graph/dump_paths.go +172 -0
- package/internal/graph/dump_paths_are_portable_and_injective_test.go +93 -0
- package/internal/graph/dump_uses_ecma_line_terminators_test.go +87 -0
- package/internal/graph/duplicate_variable_object_members_keep_first_declaration_test.go +61 -0
- package/internal/graph/edges.go +208 -0
- package/internal/graph/graph.go +143 -5
- package/internal/graph/line_model.go +122 -0
- package/internal/graph/member_relation_edges_follow_checker_validity_test.go +85 -0
- package/internal/graph/member_relation_edges_preserve_checker_boundaries_test.go +191 -0
- package/internal/graph/member_relation_edges_reject_checker_invalid_pairs_test.go +119 -0
- package/internal/graph/node_ids_preserve_hash_bearing_components_test.go +41 -0
- package/internal/graph/node_modifiers_emit_union_strings_test.go +4 -1
- package/internal/graph/object_literal_members_ride_on_the_snapshot_test.go +192 -0
- package/internal/graph/object_member_signatures_preserve_literal_whitespace_test.go +67 -0
- package/internal/graph/object_members.go +229 -0
- package/internal/graph/provenance.go +88 -30
- package/internal/graph/source_texts_cover_every_resident_program_source_test.go +73 -0
- package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +66 -0
- package/internal/graphsymbols/provider.go +26 -91
- package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +144 -0
- package/internal/lspserver/lsp_completion.go +109 -0
- package/internal/lspserver/lsp_completion_gate_never_refuses_a_matching_line_test.go +50 -0
- package/internal/lspserver/lsp_completion_hints_longest_trigger_wins_test.go +149 -0
- package/internal/lspserver/lsp_completion_hints_nearest_trigger_wins_test.go +37 -0
- package/internal/lspserver/lsp_completion_merges_into_upstream_response_test.go +149 -0
- package/internal/lspserver/lsp_completion_nearest_hint_replaces_nearest_filter_test.go +43 -0
- package/internal/lspserver/lsp_completion_preserves_completion_list_extensions_test.go +99 -0
- package/internal/lspserver/lsp_completion_refuses_hints_inside_a_string_literal_test.go +69 -0
- package/internal/lspserver/lsp_completion_replaces_full_filter_in_rank_order_test.go +85 -0
- package/internal/lspserver/lsp_completion_request.go +455 -0
- package/internal/lspserver/lsp_completion_scope.go +530 -0
- package/internal/lspserver/lsp_completion_scope_bench_test.go +41 -0
- package/internal/lspserver/lsp_completion_scope_classifies_regex_statement_and_operator_boundaries_test.go +39 -0
- package/internal/lspserver/lsp_completion_scope_ignores_jsdoc_bytes_in_literals_test.go +108 -0
- package/internal/lspserver/lsp_completion_scope_refuses_regex_hints_test.go +52 -0
- package/internal/lspserver/lsp_completion_trigger_uses_last_rune_test.go +24 -0
- package/internal/lspserver/lsp_deleted_watched_file_withdraws_plugin_diagnostics_test.go +110 -0
- package/internal/lspserver/lsp_didsave_keeps_the_incremental_changed_uri_path_test.go +78 -0
- package/internal/lspserver/lsp_hints_absence_is_not_an_error_test.go +46 -0
- package/internal/lspserver/lsp_hints_refresh_coalesces_concurrent_requests_test.go +82 -0
- package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +40 -0
- package/internal/lspserver/lsp_hints_refresh_keeps_each_producer_isolated_test.go +64 -0
- package/internal/lspserver/lsp_hints_refresh_replaces_the_session_snapshot_test.go +66 -0
- package/internal/lspserver/lsp_hints_refresh_scheduled_by_editor_events_test.go +90 -0
- package/internal/lspserver/lsp_hints_refresh_serves_completion_while_running_test.go +91 -0
- package/internal/lspserver/lsp_index_byte_from_bounds_its_search_start_test.go +38 -0
- package/internal/lspserver/lsp_initialize_constrains_position_encoding_to_utf16_test.go +205 -0
- package/internal/lspserver/lsp_late_completion_trigger_is_reported_once_test.go +105 -0
- package/internal/lspserver/lsp_native_completion_hints_decode_flat_lint_wire_test.go +66 -0
- package/internal/lspserver/lsp_native_completion_hints_grouped_compatibility_boundary_test.go +60 -0
- package/internal/lspserver/lsp_native_plugin_source.go +315 -2
- package/internal/lspserver/lsp_native_resident.go +304 -0
- package/internal/lspserver/lsp_plugin.go +85 -8
- package/internal/lspserver/lsp_plugin_completion_resolve_stays_local_test.go +82 -0
- package/internal/lspserver/lsp_position_conversions_span_astral_and_combining_runes_test.go +119 -0
- package/internal/lspserver/lsp_proxy.go +601 -9
- package/internal/lspserver/lsp_refresh.go +70 -0
- package/internal/lspserver/lsp_resident_falls_back_when_serve_unsupported_test.go +43 -0
- package/internal/lspserver/lsp_resident_maps_verb_args_test.go +34 -0
- package/internal/lspserver/lsp_server.go +13 -1
- package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +172 -0
- package/lib/TtscCompiler.d.ts +3 -1
- package/lib/TtscCompiler.js +5 -1
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/TtscService.d.ts +13 -3
- package/lib/TtscService.js +6 -6
- package/lib/TtscService.js.map +1 -1
- package/lib/compiler/internal/project/readConfigJson.d.ts +30 -0
- package/lib/compiler/internal/project/readConfigJson.js +208 -0
- package/lib/compiler/internal/project/readConfigJson.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.js +53 -149
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/residentTransformProcess.d.ts +15 -2
- package/lib/compiler/internal/residentTransformProcess.js +159 -39
- package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +6 -0
- package/lib/compiler/internal/runBuild.js +143 -44
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.d.ts +5 -1
- package/lib/compiler/internal/startResidentTransform.js +3 -1
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +12 -2
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/parser.d.ts +19 -0
- package/lib/flags/parser.js +78 -36
- package/lib/flags/parser.js.map +1 -1
- package/lib/flags/schema.d.ts +49 -11
- package/lib/flags/schema.js +94 -22
- package/lib/flags/schema.js.map +1 -1
- package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +5 -0
- package/lib/internal/assertSafeExplicitCacheDirectory.js +26 -0
- package/lib/internal/assertSafeExplicitCacheDirectory.js.map +1 -0
- package/lib/launcher/internal/runTtsc.js +107 -69
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +28 -41
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +15 -0
- package/lib/launcher/internal/runtimeHooks.js +18 -1
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/launcher/internal/watchTopology.d.ts +43 -0
- package/lib/launcher/internal/watchTopology.js +360 -0
- package/lib/launcher/internal/watchTopology.js.map +1 -0
- package/lib/plugin/internal/buildSourcePlugin.d.ts +14 -3
- package/lib/plugin/internal/buildSourcePlugin.js +247 -61
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +63 -2
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +16 -0
- package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
- package/package.json +8 -8
- package/shim/ast/shim.go +48 -3
- package/shim/astnav/go.mod +14 -0
- package/shim/astnav/go.sum +20 -0
- package/shim/astnav/shim.go +15 -0
- package/shim/checker/shim.go +100 -0
- package/shim/core/shim.go +47 -0
- package/shim/diagnosticwriter/lint.go +50 -0
- package/src/TtscCompiler.ts +5 -1
- package/src/TtscService.ts +37 -10
- package/src/compiler/internal/project/readConfigJson.ts +209 -0
- package/src/compiler/internal/project/readProjectConfig.ts +62 -158
- package/src/compiler/internal/residentTransformProcess.ts +197 -39
- package/src/compiler/internal/runBuild.ts +169 -47
- package/src/compiler/internal/startResidentTransform.ts +12 -1
- package/src/compiler/internal/transformProjectInMemory.ts +12 -1
- package/src/flags/parser.ts +113 -42
- package/src/flags/schema.ts +113 -31
- package/src/internal/assertSafeExplicitCacheDirectory.ts +29 -0
- package/src/launcher/internal/runTtsc.ts +117 -68
- package/src/launcher/internal/runTtsx.ts +29 -44
- package/src/launcher/internal/runtimeHooks.ts +30 -2
- package/src/launcher/internal/watchTopology.ts +466 -0
- package/src/plugin/internal/buildSourcePlugin.ts +326 -64
- package/src/plugin/internal/loadProjectPlugins.ts +80 -2
- package/src/structures/ITtscCompilerTransformation.ts +17 -0
- package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
- package/tools/shim_audit/baseline.json +0 -3
- package/tools/shim_audit/main.go +42 -14
- package/tools/shim_audit/shim_dir_coverage_test.go +25 -0
- package/utility/flags_gen.go +3 -3
- package/utility/host.go +9 -3
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
getString,
|
|
13
13
|
parseFlags,
|
|
14
14
|
} from "../../flags/parser";
|
|
15
|
+
import { resolveFlagSpec } from "../../flags/schema";
|
|
16
|
+
import { assertSafeExplicitCacheDirectory } from "../../internal/assertSafeExplicitCacheDirectory";
|
|
15
17
|
import {
|
|
16
18
|
isPathWithin,
|
|
17
19
|
legacyGlobalCacheTargets,
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
import type { TtscBuildOptions } from "../../structures/internal/TtscBuildOptions";
|
|
22
24
|
import { getCompilerVersionText } from "./getCompilerVersionText";
|
|
23
25
|
import { resolveCacheDir } from "./resolveCacheDir";
|
|
26
|
+
import { WatchTopology } from "./watchTopology";
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
29
|
* CLI entry point for `ttsc`. Dispatches argv to the appropriate build lane
|
|
@@ -41,13 +44,17 @@ export function runTtsc(
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
const [command, ...rest] = argv as [string, ...string[]];
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
// A leading flag is dispatched by its schema identity, so every spelling the
|
|
48
|
+
// compiler accepts (`--HELP`, `-Version`) reaches the same branch instead of
|
|
49
|
+
// falling through to the build lane where nothing reads it back out.
|
|
50
|
+
// `resolveFlagSpec` ignores dash-less tokens, so the bare `help` / `version`
|
|
51
|
+
// subcommand words below keep their own meaning.
|
|
52
|
+
const leadingFlag = resolveFlagSpec(command)?.name;
|
|
53
|
+
switch (leadingFlag ?? command) {
|
|
46
54
|
case "--help":
|
|
47
55
|
case "help":
|
|
48
56
|
printHelp();
|
|
49
57
|
return 0;
|
|
50
|
-
case "-v":
|
|
51
58
|
case "--version":
|
|
52
59
|
case "version":
|
|
53
60
|
process.stdout.write(`${getCompilerVersionText()}\n`);
|
|
@@ -66,8 +73,7 @@ export function runTtsc(
|
|
|
66
73
|
return runClean(rest);
|
|
67
74
|
case "prepare":
|
|
68
75
|
return runPrepare(rest);
|
|
69
|
-
case "
|
|
70
|
-
case "--project":
|
|
76
|
+
case "--tsconfig":
|
|
71
77
|
return runCompatibleBuild(argv, "build");
|
|
72
78
|
default:
|
|
73
79
|
if (isBuildAlias(command)) {
|
|
@@ -104,7 +110,7 @@ type TtscMode = "build" | "check" | "fix" | "format";
|
|
|
104
110
|
|
|
105
111
|
function runCompatibleBuild(argv: readonly string[], mode: TtscMode): number {
|
|
106
112
|
const checkOnly = mode !== "build";
|
|
107
|
-
const options = normalizeBuildOptions(parseBuildArgs(argv
|
|
113
|
+
const options = normalizeBuildOptions(parseBuildArgs(argv));
|
|
108
114
|
if (mode === "fix") {
|
|
109
115
|
if (options.emit === true) {
|
|
110
116
|
throw new Error("ttsc: fix and --emit are mutually exclusive");
|
|
@@ -132,16 +138,17 @@ function runCompatibleBuild(argv: readonly string[], mode: TtscMode): number {
|
|
|
132
138
|
}
|
|
133
139
|
return runWatch(options, checkOnly);
|
|
134
140
|
}
|
|
135
|
-
|
|
141
|
+
const buildOptions = checkOnly ? { ...options, emit: false } : options;
|
|
142
|
+
if (buildOptions.files.length !== 0) {
|
|
136
143
|
if (mode === "fix") {
|
|
137
144
|
throw new Error("ttsc: fix requires a project, not single-file mode");
|
|
138
145
|
}
|
|
139
146
|
if (mode === "format") {
|
|
140
147
|
throw new Error("ttsc: format requires a project, not single-file mode");
|
|
141
148
|
}
|
|
142
|
-
return runSingleFile(
|
|
149
|
+
return runSingleFile(buildOptions);
|
|
143
150
|
}
|
|
144
|
-
const result = runBuild(
|
|
151
|
+
const result = runBuild(buildOptions);
|
|
145
152
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
146
153
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
147
154
|
return result.status;
|
|
@@ -189,12 +196,18 @@ function runClean(argv: readonly string[]): number {
|
|
|
189
196
|
const options = parseProjectArgs(argv);
|
|
190
197
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
191
198
|
const projectRoot = resolveCleanProjectRoot(cwd, options.tsconfig);
|
|
192
|
-
const
|
|
199
|
+
const explicitCacheDir = options.cacheDir
|
|
200
|
+
? path.resolve(cwd, options.cacheDir)
|
|
201
|
+
: undefined;
|
|
202
|
+
if (explicitCacheDir !== undefined) {
|
|
203
|
+
assertSafeExplicitCacheDirectory(projectRoot, explicitCacheDir);
|
|
204
|
+
}
|
|
205
|
+
const targets = explicitCacheDir
|
|
193
206
|
? // Explicit `ttsc clean --cache-dir X`: the user names X as the cache to
|
|
194
207
|
// remove for this command, so remove it wholesale plus the legacy
|
|
195
208
|
// project-local caches.
|
|
196
209
|
[
|
|
197
|
-
|
|
210
|
+
explicitCacheDir,
|
|
198
211
|
path.join(projectRoot, "node_modules", ".ttsc"),
|
|
199
212
|
path.join(projectRoot, ".ttsc"),
|
|
200
213
|
]
|
|
@@ -303,8 +316,14 @@ function parseCachePathsArgs(argv: readonly string[]): {
|
|
|
303
316
|
const rest = [...argv];
|
|
304
317
|
while (rest.length !== 0) {
|
|
305
318
|
const token = rest.shift()!;
|
|
306
|
-
const [
|
|
307
|
-
|
|
319
|
+
const [rawFlag, inlineValue] = splitInlineFlag(token);
|
|
320
|
+
const flag = resolveFlagSpec(rawFlag);
|
|
321
|
+
if (flag?.subcommands.includes("cache") !== true) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
`ttsc: cache paths does not support ${JSON.stringify(token)}`,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
switch (flag.name) {
|
|
308
327
|
case "--json":
|
|
309
328
|
if (inlineValue !== undefined) {
|
|
310
329
|
throw new Error("ttsc: --json does not take a value");
|
|
@@ -312,15 +331,13 @@ function parseCachePathsArgs(argv: readonly string[]): {
|
|
|
312
331
|
out.json = true;
|
|
313
332
|
break;
|
|
314
333
|
case "--cache-dir":
|
|
315
|
-
out.cacheDir = readCachePathsValue(flag, inlineValue, rest);
|
|
334
|
+
out.cacheDir = readCachePathsValue(flag.name, inlineValue, rest);
|
|
316
335
|
break;
|
|
317
336
|
case "--cwd":
|
|
318
|
-
out.cwd = readCachePathsValue(flag, inlineValue, rest);
|
|
337
|
+
out.cwd = readCachePathsValue(flag.name, inlineValue, rest);
|
|
319
338
|
break;
|
|
320
|
-
case "-p":
|
|
321
|
-
case "--project":
|
|
322
339
|
case "--tsconfig":
|
|
323
|
-
out.tsconfig = readCachePathsValue(flag, inlineValue, rest);
|
|
340
|
+
out.tsconfig = readCachePathsValue(flag.name, inlineValue, rest);
|
|
324
341
|
break;
|
|
325
342
|
default:
|
|
326
343
|
throw new Error(
|
|
@@ -405,7 +422,7 @@ function parseProjectArgs(argv: readonly string[]) {
|
|
|
405
422
|
};
|
|
406
423
|
}
|
|
407
424
|
|
|
408
|
-
function parseBuildArgs(argv: readonly string[]
|
|
425
|
+
function parseBuildArgs(argv: readonly string[]) {
|
|
409
426
|
const result = parseFlags({
|
|
410
427
|
argv,
|
|
411
428
|
errorPrefix: "ttsc:",
|
|
@@ -418,17 +435,15 @@ function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
|
418
435
|
subcommand: "build",
|
|
419
436
|
});
|
|
420
437
|
// Defaults: pinned by the previous hand-parser. `quiet` defaults true,
|
|
421
|
-
// `--verbose` flips it to false; `emit` defaults `undefined`
|
|
422
|
-
//
|
|
438
|
+
// `--verbose` flips it to false; `emit` defaults `undefined` so the resolved
|
|
439
|
+
// project controls ordinary build mode. `runCompatibleBuild` applies the
|
|
440
|
+
// check/fix/format no-emit decision before either execution lane runs.
|
|
423
441
|
const verbose = getBoolean(result, "--verbose");
|
|
424
442
|
const quietFlag = getBoolean(result, "--quiet");
|
|
425
443
|
const quiet = verbose === true ? false : (quietFlag ?? true);
|
|
426
444
|
const explicitEmit = getBoolean(result, "--emit");
|
|
427
445
|
const explicitNoEmit = getBoolean(result, "--noEmit");
|
|
428
|
-
|
|
429
|
-
if (explicitEmit === true) emit = true;
|
|
430
|
-
else if (explicitNoEmit === true) emit = false;
|
|
431
|
-
if (emit === undefined && checkOnly) emit = false;
|
|
446
|
+
const emit = resolveExplicitEmit(explicitEmit, explicitNoEmit);
|
|
432
447
|
|
|
433
448
|
// `isPositional: looksLikeInputFile` guarantees every `result.positional`
|
|
434
449
|
// token is a TypeScript input file; forwarded flag values already live in
|
|
@@ -457,6 +472,21 @@ function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
|
457
472
|
};
|
|
458
473
|
}
|
|
459
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Collapse the two launcher-owned emit switches into the tri-state consumed by
|
|
477
|
+
* `runBuild` and the single-file lane. A specified boolean is significant even
|
|
478
|
+
* when it is `false`: `--emit=false` is analysis-only and `--noEmit=false`
|
|
479
|
+
* explicitly overrides a project's `noEmit`. `--emit` retains precedence when
|
|
480
|
+
* callers supply both switches, matching the legacy true-only resolution.
|
|
481
|
+
*/
|
|
482
|
+
function resolveExplicitEmit(
|
|
483
|
+
explicitEmit: boolean | undefined,
|
|
484
|
+
explicitNoEmit: boolean | undefined,
|
|
485
|
+
): boolean | undefined {
|
|
486
|
+
if (explicitEmit !== undefined) return explicitEmit;
|
|
487
|
+
return explicitNoEmit === undefined ? undefined : !explicitNoEmit;
|
|
488
|
+
}
|
|
489
|
+
|
|
460
490
|
/**
|
|
461
491
|
* Report whether a bare CLI token is a TypeScript source file ttsc should
|
|
462
492
|
* compile in single-file mode. Anything without a TypeScript source extension
|
|
@@ -548,12 +578,15 @@ function runSingleFile(options: ReturnType<typeof parseBuildArgs>): number {
|
|
|
548
578
|
}
|
|
549
579
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
550
580
|
const file = path.resolve(cwd, options.files[0]!);
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
581
|
+
const emit = singleFileShouldEmit(options, cwd, file);
|
|
582
|
+
const out = emit
|
|
583
|
+
? resolveSingleFileOut({
|
|
584
|
+
cliOutDir: options.outDir,
|
|
585
|
+
cwd,
|
|
586
|
+
file,
|
|
587
|
+
tsconfig: options.tsconfig,
|
|
588
|
+
})
|
|
589
|
+
: undefined;
|
|
557
590
|
const text = runSingleFileEmit({
|
|
558
591
|
binary: options.binary,
|
|
559
592
|
checkers: options.checkers,
|
|
@@ -564,14 +597,37 @@ function runSingleFile(options: ReturnType<typeof parseBuildArgs>): number {
|
|
|
564
597
|
singleThreaded: options.singleThreaded,
|
|
565
598
|
tsconfig: options.tsconfig,
|
|
566
599
|
});
|
|
567
|
-
if (!fs.existsSync(out)) {
|
|
600
|
+
if (out !== undefined && !fs.existsSync(out)) {
|
|
568
601
|
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
569
602
|
fs.writeFileSync(out, text, "utf8");
|
|
570
603
|
}
|
|
571
|
-
|
|
604
|
+
if (out !== undefined) {
|
|
605
|
+
process.stdout.write(`${path.relative(cwd, out) || path.basename(out)}\n`);
|
|
606
|
+
}
|
|
572
607
|
return 0;
|
|
573
608
|
}
|
|
574
609
|
|
|
610
|
+
/**
|
|
611
|
+
* Resolve the same effective emit decision used by the project lane before
|
|
612
|
+
* entering the single-file compatibility path. The compatibility path still
|
|
613
|
+
* emits into a private temporary directory to obtain transformed text and
|
|
614
|
+
* diagnostics, but only this boundary is allowed to write into the user's
|
|
615
|
+
* tree.
|
|
616
|
+
*/
|
|
617
|
+
function singleFileShouldEmit(
|
|
618
|
+
options: ReturnType<typeof parseBuildArgs>,
|
|
619
|
+
cwd: string,
|
|
620
|
+
file: string,
|
|
621
|
+
): boolean {
|
|
622
|
+
if (options.emit !== undefined) return options.emit;
|
|
623
|
+
const project = readProjectConfig({
|
|
624
|
+
cwd,
|
|
625
|
+
file,
|
|
626
|
+
tsconfig: options.tsconfig,
|
|
627
|
+
});
|
|
628
|
+
return project.compilerOptions.noEmit !== true;
|
|
629
|
+
}
|
|
630
|
+
|
|
575
631
|
function resolveSingleFileOut(opts: {
|
|
576
632
|
cliOutDir?: string;
|
|
577
633
|
cwd: string;
|
|
@@ -672,9 +728,13 @@ function runWatch(
|
|
|
672
728
|
checkOnly: boolean,
|
|
673
729
|
): number {
|
|
674
730
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
731
|
+
let topology: WatchTopology | undefined;
|
|
675
732
|
const invocation = {
|
|
676
|
-
...options,
|
|
733
|
+
...(checkOnly ? { ...options, emit: false } : options),
|
|
677
734
|
cwd,
|
|
735
|
+
onWatchInputs: (inputs: readonly string[]) => {
|
|
736
|
+
topology?.setExtraInputs(inputs);
|
|
737
|
+
},
|
|
678
738
|
quiet: true,
|
|
679
739
|
};
|
|
680
740
|
const root = path.dirname(
|
|
@@ -692,6 +752,7 @@ function runWatch(
|
|
|
692
752
|
|
|
693
753
|
const runOnce = () => {
|
|
694
754
|
running = true;
|
|
755
|
+
let completed = false;
|
|
695
756
|
try {
|
|
696
757
|
if (!options.preserveWatchOutput) {
|
|
697
758
|
process.stdout.write("\x1bc");
|
|
@@ -717,6 +778,7 @@ function runWatch(
|
|
|
717
778
|
return result.status;
|
|
718
779
|
})();
|
|
719
780
|
lastStatus = status;
|
|
781
|
+
completed = true;
|
|
720
782
|
process.stdout.write(
|
|
721
783
|
`[ttsc] ${status === 0 ? "watch build complete" : "watch build failed"}\n`,
|
|
722
784
|
);
|
|
@@ -726,6 +788,19 @@ function runWatch(
|
|
|
726
788
|
process.stdout.write(`[ttsc] watch build failed\n`);
|
|
727
789
|
} finally {
|
|
728
790
|
running = false;
|
|
791
|
+
if (completed) {
|
|
792
|
+
try {
|
|
793
|
+
// A filesystem event can arrive after the build reports completion
|
|
794
|
+
// but before this synchronous cleanup reaches its re-resolution.
|
|
795
|
+
// Notify for a membership change here as well, otherwise that new
|
|
796
|
+
// input is silently absorbed into the watch set without a rebuild.
|
|
797
|
+
topology?.refresh(true);
|
|
798
|
+
} catch (error) {
|
|
799
|
+
process.stderr.write(
|
|
800
|
+
`[ttsc] watch error on ${path.relative(cwd, root) || "."}: ${formatError(error)}\n`,
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
729
804
|
}
|
|
730
805
|
if (rerun) {
|
|
731
806
|
rerun = false;
|
|
@@ -741,23 +816,20 @@ function runWatch(
|
|
|
741
816
|
timer = setTimeout(runOnce, 60);
|
|
742
817
|
};
|
|
743
818
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
const watcher = fs.watch(dir, { persistent: true }, () => trigger());
|
|
747
|
-
// Without an error handler Node rethrows watcher errors as uncaught
|
|
748
|
-
// exceptions, which would crash the session. inotify limits (ENOSPC) and
|
|
749
|
-
// transient FS errors should be logged while the session stays alive.
|
|
750
|
-
watcher.on("error", (err) => {
|
|
819
|
+
topology = new WatchTopology(invocation, {
|
|
820
|
+
onError: (location, error) => {
|
|
751
821
|
process.stderr.write(
|
|
752
|
-
`[ttsc] watch error on ${path.relative(cwd,
|
|
822
|
+
`[ttsc] watch error on ${path.relative(cwd, location) || "."}: ${formatError(error)}\n`,
|
|
753
823
|
);
|
|
754
|
-
}
|
|
755
|
-
|
|
824
|
+
},
|
|
825
|
+
onInputChange: trigger,
|
|
826
|
+
onTopologyChange: trigger,
|
|
756
827
|
});
|
|
828
|
+
topology.refresh(false);
|
|
757
829
|
|
|
758
830
|
const close = () => {
|
|
759
831
|
if (timer) clearTimeout(timer);
|
|
760
|
-
|
|
832
|
+
topology?.close();
|
|
761
833
|
};
|
|
762
834
|
process.on("SIGINT", () => {
|
|
763
835
|
close();
|
|
@@ -789,29 +861,6 @@ function toExitCode(status: number): number {
|
|
|
789
861
|
return Number.isInteger(status) && status >= 0 && status <= 255 ? status : 1;
|
|
790
862
|
}
|
|
791
863
|
|
|
792
|
-
function collectWatchDirectories(root: string): string[] {
|
|
793
|
-
const out: string[] = [];
|
|
794
|
-
const stack: string[] = [root];
|
|
795
|
-
while (stack.length !== 0) {
|
|
796
|
-
const current = stack.pop()!;
|
|
797
|
-
out.push(current);
|
|
798
|
-
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
799
|
-
if (
|
|
800
|
-
entry.name === "node_modules" ||
|
|
801
|
-
entry.name === ".git" ||
|
|
802
|
-
entry.name === "lib" ||
|
|
803
|
-
entry.name === "dist"
|
|
804
|
-
) {
|
|
805
|
-
continue;
|
|
806
|
-
}
|
|
807
|
-
if (entry.isDirectory()) {
|
|
808
|
-
stack.push(path.join(current, entry.name));
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
return out;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
864
|
function formatError(error: unknown): string {
|
|
816
865
|
if (error instanceof Error) {
|
|
817
866
|
return error.message;
|
|
@@ -7,8 +7,10 @@ import {
|
|
|
7
7
|
getBoolean,
|
|
8
8
|
getNumber,
|
|
9
9
|
getString,
|
|
10
|
+
getStringList,
|
|
10
11
|
parseFlags,
|
|
11
12
|
} from "../../flags/parser";
|
|
13
|
+
import { resolveFlagSpec } from "../../flags/schema";
|
|
12
14
|
import { getCompilerVersionText } from "./getCompilerVersionText";
|
|
13
15
|
import { prepareExecution } from "./prepareExecution";
|
|
14
16
|
import { resolveCacheDir } from "./resolveCacheDir";
|
|
@@ -98,26 +100,23 @@ function parseCLI(argv: readonly string[]) {
|
|
|
98
100
|
// tokens (entry file + flag values that aren't `.ts`) and a passthrough
|
|
99
101
|
// list mirroring the pre-schema behaviour.
|
|
100
102
|
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
token === "-P"
|
|
108
|
-
? "--project"
|
|
109
|
-
: token.startsWith("-P=")
|
|
110
|
-
? `--project=${token.slice("-P=".length)}`
|
|
111
|
-
: token,
|
|
112
|
-
);
|
|
103
|
+
// The legacy uppercase `-P` spelling ttsx has always accepted needs no
|
|
104
|
+
// rewrite: the engine resolves a token to the flag the compiler resolves it
|
|
105
|
+
// to, so `-P` and `-P=<file>` reach `--tsconfig` by the same rule that makes
|
|
106
|
+
// `-p` reach it. A textual pre-rewrite here would be a second rule for a job
|
|
107
|
+
// the engine owns.
|
|
108
|
+
//
|
|
113
109
|
// Terminal flags (--help / --version) short-circuit before parsing so
|
|
114
|
-
// ttsx prints help text even when the entry file is missing.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
// ttsx prints help text even when the entry file is missing. Resolved
|
|
111
|
+
// through the schema so every spelling the compiler accepts (`--HELP`,
|
|
112
|
+
// `-Version`) reaches the same branch.
|
|
113
|
+
for (const token of argv) {
|
|
114
|
+
const flag = resolveFlagSpec(token)?.name;
|
|
115
|
+
if (flag === "--help") return "help" as const;
|
|
116
|
+
if (flag === "--version") return "version" as const;
|
|
118
117
|
}
|
|
119
118
|
const result = parseFlags({
|
|
120
|
-
argv
|
|
119
|
+
argv,
|
|
121
120
|
errorPrefix: "ttsx:",
|
|
122
121
|
forwardAfterFirstPositional: true,
|
|
123
122
|
honorDoubleDashSeparator: true,
|
|
@@ -145,34 +144,20 @@ function parseCLI(argv: readonly string[]) {
|
|
|
145
144
|
// which MUST NOT reach tsgo.
|
|
146
145
|
const postEntryArgs: string[] = [...result.tail];
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
// raw argv. Mirrors the legacy parser's `preload.push(takeValue(...))`
|
|
152
|
-
// behaviour.
|
|
147
|
+
// `--require` is declared `repeatable`, so the engine records every accepted
|
|
148
|
+
// value in argv order and the launcher reads the list straight off the parse
|
|
149
|
+
// result.
|
|
153
150
|
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
for (let i = 0; i < scanLimit; i += 1) {
|
|
165
|
-
const token = rewritten[i]!;
|
|
166
|
-
if (token === "-r" || token === "--require") {
|
|
167
|
-
const value = rewritten[i + 1];
|
|
168
|
-
if (value !== undefined && !value.startsWith("-")) {
|
|
169
|
-
preload.push(value);
|
|
170
|
-
i += 1;
|
|
171
|
-
}
|
|
172
|
-
} else if (token.startsWith("--require=")) {
|
|
173
|
-
preload.push(token.slice("--require=".length));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
151
|
+
// This replaces a second, hand-written scan over raw argv that re-derived the
|
|
152
|
+
// pre-entry boundary with `looksLikeEntryFile`. Applied to raw tokens that
|
|
153
|
+
// predicate cannot tell an entry from a `--require` value carrying a
|
|
154
|
+
// TypeScript extension, nor from an inline `--require=<x>.ts` token, so the
|
|
155
|
+
// scan stopped before the tokens it existed to collect and preloads were
|
|
156
|
+
// dropped silently. The engine already owns that boundary:
|
|
157
|
+
// `forwardAfterFirstPositional` routes every post-entry token to
|
|
158
|
+
// `result.tail` without parsing it, so `ttsx entry.ts -r preload.cjs` still
|
|
159
|
+
// forwards the pair to the program instead of preloading it.
|
|
160
|
+
const preload = getStringList(result, "--require");
|
|
176
161
|
|
|
177
162
|
return {
|
|
178
163
|
binary: getString(result, "--binary"),
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Module,
|
|
5
|
+
isBuiltin,
|
|
6
|
+
registerHooks,
|
|
7
|
+
stripTypeScriptTypes,
|
|
8
|
+
} from "node:module";
|
|
4
9
|
import os from "node:os";
|
|
5
10
|
import path from "node:path";
|
|
6
11
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -294,7 +299,10 @@ function resolve(
|
|
|
294
299
|
): ResolveResult {
|
|
295
300
|
try {
|
|
296
301
|
return rememberCommonJsNamedInterop(
|
|
297
|
-
|
|
302
|
+
restoreStrippedNodeBuiltinScheme(
|
|
303
|
+
specifier,
|
|
304
|
+
nextResolve(specifier, context),
|
|
305
|
+
),
|
|
298
306
|
context,
|
|
299
307
|
);
|
|
300
308
|
} catch (error) {
|
|
@@ -309,6 +317,26 @@ function resolve(
|
|
|
309
317
|
}
|
|
310
318
|
}
|
|
311
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Restore a `node:` builtin URL when affected Node releases return the exact
|
|
322
|
+
* prefix-stripped spelling from their synchronous CommonJS resolver.
|
|
323
|
+
*
|
|
324
|
+
* Every other result passes through unchanged. In particular, a user hook that
|
|
325
|
+
* intentionally remaps a `node:` specifier to another URL retains ownership of
|
|
326
|
+
* that mapping, while ordinary and ESM builtin results already carrying the
|
|
327
|
+
* scheme avoid an unnecessary copy.
|
|
328
|
+
*/
|
|
329
|
+
export function restoreStrippedNodeBuiltinScheme(
|
|
330
|
+
specifier: string,
|
|
331
|
+
result: ResolveResult,
|
|
332
|
+
): ResolveResult {
|
|
333
|
+
return isBuiltin(specifier) &&
|
|
334
|
+
specifier.startsWith("node:") &&
|
|
335
|
+
result.url === specifier.slice("node:".length)
|
|
336
|
+
? { ...result, url: specifier }
|
|
337
|
+
: result;
|
|
338
|
+
}
|
|
339
|
+
|
|
312
340
|
/** Cache of built projects keyed by owning tsconfig path. */
|
|
313
341
|
interface BuiltProject {
|
|
314
342
|
emitDir: string;
|